@bendyline/squisq-formats 2.1.0 → 2.2.0
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 +21 -0
- package/NOTICE.md +20 -0
- package/README.md +1 -1
- package/dist/{chunk-NNHKUXKA.js → chunk-26ISNJ7Y.js} +85 -65
- package/dist/{chunk-NKAJPJ4G.js → chunk-2JJ5RFDZ.js} +0 -1
- package/dist/{chunk-WQSHGBLN.js → chunk-3NKXBZSR.js} +193 -42
- package/dist/{chunk-KURGXM4I.js → chunk-4V3KCHAP.js} +3 -4
- package/dist/{chunk-MLX2BOJC.js → chunk-6RQOV3B3.js} +1 -2
- package/dist/{chunk-EW54IRRS.js → chunk-6S6GU3ZG.js} +5 -6
- package/dist/{chunk-FE6OJV6O.js → chunk-7AWFHP5U.js} +1 -1
- package/dist/{chunk-RFAPOKHJ.js → chunk-AD2WT564.js} +59 -9
- package/dist/{chunk-O3GVVND4.js → chunk-AONELFLA.js} +0 -1
- package/dist/{chunk-SC67HYQJ.js → chunk-EJTNGKEA.js} +5 -8
- package/dist/chunk-GX7RAUME.js +121 -0
- package/dist/{chunk-SSUPBUF5.js → chunk-IIQYS2YH.js} +0 -1
- package/dist/{chunk-RLU7UFYU.js → chunk-IPN56VLW.js} +83 -58
- package/dist/{chunk-DTDF6QDP.js → chunk-JE6LSIHE.js} +81 -20
- package/dist/{chunk-U4MRIFKL.js → chunk-JU2RHXUB.js} +0 -1
- package/dist/{chunk-4VUWTSGM.js → chunk-K6XRMVPW.js} +64 -31
- package/dist/{chunk-ODL3SSPT.js → chunk-KXOZMWBS.js} +0 -1
- package/dist/chunk-OGS5VCGJ.js +446 -0
- package/dist/{chunk-GVS2XXV6.js → chunk-PJXJI2LY.js} +449 -57
- package/dist/{chunk-74GO3FVS.js → chunk-PU7REGWV.js} +5 -8
- package/dist/{chunk-PN52A5AA.js → chunk-SBUW7NHR.js} +0 -1
- package/dist/{chunk-QFLDYKCR.js → chunk-TAAENIRB.js} +5 -8
- package/dist/{chunk-7ARKUCQT.js → chunk-X2DEAXNK.js} +54 -2
- package/dist/container/index.js +1 -2
- package/dist/csv/index.d.ts +27 -2
- package/dist/csv/index.js +1 -2
- package/dist/docx/index.d.ts +5 -1
- package/dist/docx/index.js +9 -11
- package/dist/epub/index.d.ts +2 -0
- package/dist/epub/index.js +5 -6
- package/dist/{export-D2NkylDT.d.ts → export-D9msROJS.d.ts} +18 -6
- package/dist/extract-MN7LA3NL.js +13 -0
- package/dist/html/index.d.ts +11 -4
- package/dist/html/index.js +3 -4
- package/dist/images-ESPQKVTW.js +6 -0
- package/dist/{import-K8mfc0fz.d.ts → import-C3htUTss.d.ts} +5 -1
- package/dist/{import-DTkDxHmZ.d.ts → import-C8whCC7_.d.ts} +6 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.js +28 -26
- package/dist/infer/index.d.ts +3 -3
- package/dist/infer/index.js +7 -9
- package/dist/{layouts-BHrgZ5FS.d.ts → layouts-CTdPlB-u.d.ts} +1 -1
- package/dist/layouts-DRWZGSPD.js +10 -0
- package/dist/{mapTheme-IR27S6IV.js → mapTheme-4TWH25FT.js} +1 -2
- package/dist/ooxml/index.d.ts +3 -3
- package/dist/ooxml/index.js +14 -13
- package/dist/pdf/index.d.ts +18 -0
- package/dist/pdf/index.js +2 -3
- package/dist/pptx/index.d.ts +4 -4
- package/dist/pptx/index.js +11 -13
- package/dist/{reader-B9L8Ucbj.d.ts → reader-B_m1aKZC.d.ts} +30 -1
- package/dist/registry/index.d.ts +21 -5
- package/dist/registry/index.js +9 -6
- package/dist/{themeReader-DJKErl_j.d.ts → themeReader-DCtwC83Q.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +3 -3
- package/dist/xlsx/index.js +6 -7
- package/package.json +6 -3
- package/dist/chunk-4VUWTSGM.js.map +0 -1
- package/dist/chunk-6M7Z25LA.js +0 -46
- package/dist/chunk-6M7Z25LA.js.map +0 -1
- package/dist/chunk-74GO3FVS.js.map +0 -1
- package/dist/chunk-7ARKUCQT.js.map +0 -1
- package/dist/chunk-DTDF6QDP.js.map +0 -1
- package/dist/chunk-EW54IRRS.js.map +0 -1
- package/dist/chunk-FE6OJV6O.js.map +0 -1
- package/dist/chunk-GVS2XXV6.js.map +0 -1
- package/dist/chunk-KURGXM4I.js.map +0 -1
- package/dist/chunk-MLX2BOJC.js.map +0 -1
- package/dist/chunk-NKAJPJ4G.js.map +0 -1
- package/dist/chunk-NNHKUXKA.js.map +0 -1
- package/dist/chunk-O3GVVND4.js.map +0 -1
- package/dist/chunk-ODL3SSPT.js.map +0 -1
- package/dist/chunk-PN52A5AA.js.map +0 -1
- package/dist/chunk-QFLDYKCR.js.map +0 -1
- package/dist/chunk-RFAPOKHJ.js.map +0 -1
- package/dist/chunk-RLU7UFYU.js.map +0 -1
- package/dist/chunk-SC67HYQJ.js.map +0 -1
- package/dist/chunk-SSUPBUF5.js.map +0 -1
- package/dist/chunk-U4MRIFKL.js.map +0 -1
- package/dist/chunk-UGYF5AZE.js +0 -275
- package/dist/chunk-UGYF5AZE.js.map +0 -1
- package/dist/chunk-WQSHGBLN.js.map +0 -1
- package/dist/chunk-YRT7GQ5Y.js +0 -28
- package/dist/chunk-YRT7GQ5Y.js.map +0 -1
- package/dist/container/index.js.map +0 -1
- package/dist/csv/index.js.map +0 -1
- package/dist/docx/index.js.map +0 -1
- package/dist/epub/index.js.map +0 -1
- package/dist/extract-OJ7ZQV6P.js +0 -15
- package/dist/extract-OJ7ZQV6P.js.map +0 -1
- package/dist/html/index.js.map +0 -1
- package/dist/images-7FBWPKE3.js +0 -7
- package/dist/images-7FBWPKE3.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/infer/index.js.map +0 -1
- package/dist/layouts-5VDIRPIJ.js +0 -12
- package/dist/layouts-5VDIRPIJ.js.map +0 -1
- package/dist/mapTheme-IR27S6IV.js.map +0 -1
- package/dist/ooxml/index.js.map +0 -1
- package/dist/pdf/index.js.map +0 -1
- package/dist/pptx/index.js.map +0 -1
- package/dist/registry/index.js.map +0 -1
- package/dist/xlsx/index.js.map +0 -1
- package/src/__tests__/container.test.ts +0 -230
- package/src/__tests__/convert.test.ts +0 -495
- package/src/__tests__/csvImport.test.ts +0 -84
- package/src/__tests__/docxExport.test.ts +0 -491
- package/src/__tests__/docxImport.test.ts +0 -531
- package/src/__tests__/epub.test.ts +0 -649
- package/src/__tests__/exportThemeReconciliation.test.ts +0 -87
- package/src/__tests__/formatRegistry.test.ts +0 -174
- package/src/__tests__/html.test.ts +0 -439
- package/src/__tests__/htmlImport.test.ts +0 -57
- package/src/__tests__/inferTheme.test.ts +0 -135
- package/src/__tests__/lossyWarnings.test.ts +0 -146
- package/src/__tests__/ooxml.test.ts +0 -271
- package/src/__tests__/ooxmlCancellation.test.ts +0 -113
- package/src/__tests__/ooxmlThemeReader.test.ts +0 -92
- package/src/__tests__/pdfExport.test.ts +0 -322
- package/src/__tests__/pdfImport.test.ts +0 -384
- package/src/__tests__/plainHtml.test.ts +0 -417
- package/src/__tests__/plainHtmlBundle.test.ts +0 -253
- package/src/__tests__/pptxExport.test.ts +0 -138
- package/src/__tests__/pptxImport.test.ts +0 -145
- package/src/__tests__/pptxInferFixtures.ts +0 -314
- package/src/__tests__/pptxLayoutInfer.test.ts +0 -395
- package/src/__tests__/roundTrip.test.ts +0 -201
- package/src/__tests__/roundTripAssets.test.ts +0 -50
- package/src/__tests__/roundTripMatrix.fixtures.ts +0 -86
- package/src/__tests__/roundTripMatrix.helpers.ts +0 -154
- package/src/__tests__/roundTripMatrix.test.ts +0 -142
- package/src/__tests__/sharedContainer.test.ts +0 -41
- package/src/__tests__/sharedImages.test.ts +0 -61
- package/src/__tests__/xlsxExport.test.ts +0 -164
- package/src/__tests__/xlsxImport.test.ts +0 -80
- package/src/__tests__/zipSafety.test.ts +0 -317
- package/src/container/index.ts +0 -94
- package/src/csv/index.ts +0 -188
- package/src/docx/export.ts +0 -1375
- package/src/docx/import.ts +0 -1250
- package/src/docx/index.ts +0 -26
- package/src/docx/styles.ts +0 -145
- package/src/epub/export.ts +0 -968
- package/src/epub/index.ts +0 -20
- package/src/html/docsHtmlBundle.ts +0 -373
- package/src/html/htmlTemplate.ts +0 -385
- package/src/html/imageUtils.ts +0 -61
- package/src/html/import.ts +0 -297
- package/src/html/index.ts +0 -212
- package/src/html/plainHtml.ts +0 -790
- package/src/html/plainHtmlBundle.ts +0 -421
- package/src/index.ts +0 -109
- package/src/infer/extract.ts +0 -127
- package/src/infer/index.ts +0 -199
- package/src/infer/mapTheme.ts +0 -176
- package/src/infer/types.ts +0 -27
- package/src/ooxml/index.ts +0 -111
- package/src/ooxml/namespaces.ts +0 -217
- package/src/ooxml/readUtils.ts +0 -44
- package/src/ooxml/reader.ts +0 -318
- package/src/ooxml/themeReader.ts +0 -197
- package/src/ooxml/types.ts +0 -103
- package/src/ooxml/writer.ts +0 -339
- package/src/ooxml/xmlUtils.ts +0 -123
- package/src/pdf/export.ts +0 -1084
- package/src/pdf/import.ts +0 -1164
- package/src/pdf/index.ts +0 -29
- package/src/pdf/styles.ts +0 -180
- package/src/pptx/export.ts +0 -1184
- package/src/pptx/import.ts +0 -455
- package/src/pptx/index.ts +0 -52
- package/src/pptx/layouts.ts +0 -1222
- package/src/pptx/styles.ts +0 -96
- package/src/pptx/templates.ts +0 -187
- package/src/registry/convert.ts +0 -433
- package/src/registry/defaultFormats.ts +0 -413
- package/src/registry/errors.ts +0 -46
- package/src/registry/index.ts +0 -43
- package/src/registry/registry.ts +0 -48
- package/src/registry/types.ts +0 -170
- package/src/shared/boundedZipArchive.ts +0 -383
- package/src/shared/container.ts +0 -28
- package/src/shared/fidelity.ts +0 -130
- package/src/shared/images.ts +0 -44
- package/src/shared/inlineRuns.ts +0 -99
- package/src/shared/text.ts +0 -41
- package/src/shared/zipEntryCount.ts +0 -151
- package/src/shared/zipLimits.ts +0 -296
- package/src/shared/zipSafety.ts +0 -19
- package/src/xlsx/export.ts +0 -253
- package/src/xlsx/import.ts +0 -160
- package/src/xlsx/index.ts +0 -35
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractPlainText
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-SBUW7NHR.js";
|
|
4
4
|
import {
|
|
5
5
|
escapeXml
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JU2RHXUB.js";
|
|
7
7
|
import {
|
|
8
8
|
extractFilename,
|
|
9
9
|
inferMimeType
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6RQOV3B3.js";
|
|
11
11
|
|
|
12
12
|
// src/epub/export.ts
|
|
13
13
|
import JSZip from "jszip";
|
|
@@ -15,6 +15,7 @@ import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
|
15
15
|
import { resolveThemeForDoc } from "@bendyline/squisq/doc";
|
|
16
16
|
import { readFrontmatterThemeId, sanitizeUrl } from "@bendyline/squisq/markdown";
|
|
17
17
|
async function markdownDocToEpub(doc, options = {}) {
|
|
18
|
+
options.signal?.throwIfAborted();
|
|
18
19
|
const fmTitle = doc.frontmatter?.title;
|
|
19
20
|
const fmAuthor = doc.frontmatter?.author;
|
|
20
21
|
const title = options.title ?? (typeof fmTitle === "string" ? fmTitle : "Untitled");
|
|
@@ -28,7 +29,9 @@ async function markdownDocToEpub(doc, options = {}) {
|
|
|
28
29
|
const resolvedImages = /* @__PURE__ */ new Map();
|
|
29
30
|
const usedImageNames = /* @__PURE__ */ new Set();
|
|
30
31
|
if (options.images) {
|
|
32
|
+
let imageIndex = 0;
|
|
31
33
|
for (const src of imageEntries) {
|
|
34
|
+
if ((imageIndex++ & 63) === 0) options.signal?.throwIfAborted();
|
|
32
35
|
const data = options.images.get(src);
|
|
33
36
|
if (data) {
|
|
34
37
|
const filename = uniqueFilename(safeArchiveBasename(src, "image"), usedImageNames);
|
|
@@ -47,6 +50,7 @@ async function markdownDocToEpub(doc, options = {}) {
|
|
|
47
50
|
zip.file("META-INF/container.xml", CONTAINER_XML);
|
|
48
51
|
zip.file("OEBPS/styles.css", css);
|
|
49
52
|
for (const [, img] of resolvedImages) {
|
|
53
|
+
options.signal?.throwIfAborted();
|
|
50
54
|
zip.file(`OEBPS/images/${img.filename}`, img.data);
|
|
51
55
|
}
|
|
52
56
|
let coverFilename;
|
|
@@ -134,12 +138,17 @@ async function markdownDocToEpub(doc, options = {}) {
|
|
|
134
138
|
const id = `chapter-${num}`;
|
|
135
139
|
const filename = `${id}.xhtml`;
|
|
136
140
|
const audioInfo = chapterAudio[i] ?? null;
|
|
137
|
-
const xhtml = renderChapterXhtml(
|
|
141
|
+
const { xhtml, ids } = renderChapterXhtml(
|
|
142
|
+
chap.nodes,
|
|
143
|
+
title,
|
|
144
|
+
resolvedImages,
|
|
145
|
+
audioInfo !== null
|
|
146
|
+
);
|
|
138
147
|
zip.file(`OEBPS/chapters/${filename}`, xhtml);
|
|
139
148
|
let smilFilename;
|
|
140
149
|
if (audioInfo) {
|
|
141
150
|
smilFilename = `${id}.smil`;
|
|
142
|
-
const smil = generateSmil(filename, audioInfo,
|
|
151
|
+
const smil = generateSmil(filename, audioInfo, ids);
|
|
143
152
|
zip.file(`OEBPS/chapters/${smilFilename}`, smil);
|
|
144
153
|
}
|
|
145
154
|
chapterFiles.push({
|
|
@@ -248,10 +257,14 @@ function collectDocImages(nodes) {
|
|
|
248
257
|
return images;
|
|
249
258
|
}
|
|
250
259
|
function renderChapterXhtml(nodes, bookTitle, images, addIds = false) {
|
|
251
|
-
|
|
252
|
-
const nextId = () =>
|
|
260
|
+
const ids = [];
|
|
261
|
+
const nextId = () => {
|
|
262
|
+
const id = `p${ids.length + 1}`;
|
|
263
|
+
ids.push(id);
|
|
264
|
+
return id;
|
|
265
|
+
};
|
|
253
266
|
const body = nodes.map((n) => blockToXhtml(n, images, addIds ? nextId : void 0)).join("\n");
|
|
254
|
-
|
|
267
|
+
const xhtml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
255
268
|
<!DOCTYPE html>
|
|
256
269
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
|
257
270
|
<head>
|
|
@@ -263,40 +276,46 @@ function renderChapterXhtml(nodes, bookTitle, images, addIds = false) {
|
|
|
263
276
|
${body}
|
|
264
277
|
</body>
|
|
265
278
|
</html>`;
|
|
279
|
+
return { xhtml, ids };
|
|
266
280
|
}
|
|
267
281
|
function blockToXhtml(node, images, nextId) {
|
|
268
|
-
|
|
282
|
+
let cached;
|
|
283
|
+
const idAttr = () => {
|
|
284
|
+
if (!nextId) return "";
|
|
285
|
+
cached ?? (cached = ` id="${nextId()}"`);
|
|
286
|
+
return cached;
|
|
287
|
+
};
|
|
269
288
|
switch (node.type) {
|
|
270
289
|
case "heading": {
|
|
271
290
|
const tag = `h${node.depth}`;
|
|
272
|
-
return `<${tag}${idAttr}>${inlinesToXhtml(node.children, images)}</${tag}>`;
|
|
291
|
+
return `<${tag}${idAttr()}>${inlinesToXhtml(node.children, images)}</${tag}>`;
|
|
273
292
|
}
|
|
274
293
|
case "paragraph":
|
|
275
|
-
return `<p${idAttr}>${inlinesToXhtml(node.children, images)}</p>`;
|
|
294
|
+
return `<p${idAttr()}>${inlinesToXhtml(node.children, images)}</p>`;
|
|
276
295
|
case "blockquote":
|
|
277
|
-
return `<blockquote${idAttr}>
|
|
296
|
+
return `<blockquote${idAttr()}>
|
|
278
297
|
${node.children.map((c) => blockToXhtml(c, images, nextId)).join("\n")}
|
|
279
298
|
</blockquote>`;
|
|
280
299
|
case "list": {
|
|
281
300
|
const tag = node.ordered ? "ol" : "ul";
|
|
282
301
|
const startAttr = node.ordered && node.start && node.start !== 1 ? ` start="${node.start}"` : "";
|
|
283
302
|
const items = node.children.map((item) => listItemToXhtml(item, images)).join("\n");
|
|
284
|
-
return `<${tag}${idAttr}${startAttr}>
|
|
303
|
+
return `<${tag}${idAttr()}${startAttr}>
|
|
285
304
|
${items}
|
|
286
305
|
</${tag}>`;
|
|
287
306
|
}
|
|
288
307
|
case "code": {
|
|
289
308
|
const langAttr = node.lang ? ` class="language-${escapeXml(node.lang)}"` : "";
|
|
290
|
-
return `<pre${idAttr}><code${langAttr}>${escapeXml(node.value)}</code></pre>`;
|
|
309
|
+
return `<pre${idAttr()}><code${langAttr}>${escapeXml(node.value)}</code></pre>`;
|
|
291
310
|
}
|
|
292
311
|
case "thematicBreak":
|
|
293
|
-
return `<hr${idAttr}/>`;
|
|
312
|
+
return `<hr${idAttr()}/>`;
|
|
294
313
|
case "table":
|
|
295
|
-
return tableToXhtml(node, images, idAttr);
|
|
314
|
+
return tableToXhtml(node, images, idAttr());
|
|
296
315
|
case "htmlBlock":
|
|
297
|
-
return `<p${idAttr}>${escapeXml(node.rawHtml.replace(/<[^>]+>/g, ""))}</p>`;
|
|
316
|
+
return `<p${idAttr()}>${escapeXml(node.rawHtml.replace(/<[^>]+>/g, ""))}</p>`;
|
|
298
317
|
case "math":
|
|
299
|
-
return `<p${idAttr} class="math">${escapeXml(node.value)}</p>`;
|
|
318
|
+
return `<p${idAttr()} class="math">${escapeXml(node.value)}</p>`;
|
|
300
319
|
default:
|
|
301
320
|
return "";
|
|
302
321
|
}
|
|
@@ -386,6 +405,14 @@ function safeArchiveBasename(path, fallback) {
|
|
|
386
405
|
}).join("").trim();
|
|
387
406
|
return !safe || safe === "." || safe === ".." ? fallback : safe;
|
|
388
407
|
}
|
|
408
|
+
function uniqueManifestId(prefix, filename, used) {
|
|
409
|
+
const base = `${prefix}-${filename.replace(/[^a-zA-Z0-9]/g, "-")}`;
|
|
410
|
+
let id = base;
|
|
411
|
+
let suffix = 2;
|
|
412
|
+
while (used.has(id)) id = `${base}-${suffix++}`;
|
|
413
|
+
used.add(id);
|
|
414
|
+
return id;
|
|
415
|
+
}
|
|
389
416
|
function uniqueFilename(filename, used) {
|
|
390
417
|
if (!used.has(filename)) return filename;
|
|
391
418
|
const dot = filename.lastIndexOf(".");
|
|
@@ -395,22 +422,24 @@ function uniqueFilename(filename, used) {
|
|
|
395
422
|
while (used.has(`${base}-${suffix}${ext}`)) suffix++;
|
|
396
423
|
return `${base}-${suffix}${ext}`;
|
|
397
424
|
}
|
|
398
|
-
function generateSmil(chapterFilename, audioInfo,
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
425
|
+
function generateSmil(chapterFilename, audioInfo, elementIds) {
|
|
426
|
+
if (elementIds.length === 0) {
|
|
427
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
428
|
+
<smil xmlns="http://www.w3.org/ns/SMIL" xmlns:epub="http://www.idpf.org/2007/ops" version="3.0">
|
|
429
|
+
<body>
|
|
430
|
+
<seq id="seq-1" epub:textref="${chapterFilename}">
|
|
431
|
+
</seq>
|
|
432
|
+
</body>
|
|
433
|
+
</smil>`;
|
|
403
434
|
}
|
|
404
|
-
|
|
405
|
-
if (elementCount === 0) elementCount = 1;
|
|
406
|
-
const clipDuration = audioInfo.duration / elementCount;
|
|
435
|
+
const clipDuration = audioInfo.duration / elementIds.length;
|
|
407
436
|
const pars = [];
|
|
408
|
-
for (let i = 0; i <
|
|
437
|
+
for (let i = 0; i < elementIds.length; i++) {
|
|
409
438
|
const clipStart = formatTime(audioInfo.clipStart + i * clipDuration, true);
|
|
410
439
|
const clipEnd = formatTime(audioInfo.clipStart + (i + 1) * clipDuration, true);
|
|
411
440
|
pars.push(
|
|
412
441
|
` <par id="par-${i + 1}">
|
|
413
|
-
<text src="${chapterFilename}
|
|
442
|
+
<text src="${chapterFilename}#${escapeXml(elementIds[i])}"/>
|
|
414
443
|
<audio src="../audio/${escapeXml(audioInfo.audioFilename)}" clipBegin="${clipStart}" clipEnd="${clipEnd}"/>
|
|
415
444
|
</par>`
|
|
416
445
|
);
|
|
@@ -477,12 +506,17 @@ function generateContentOpf(params) {
|
|
|
477
506
|
);
|
|
478
507
|
}
|
|
479
508
|
}
|
|
509
|
+
const usedManifestIds = /* @__PURE__ */ new Set(["cover-image", "cover-page"]);
|
|
510
|
+
for (const chap of chapters) {
|
|
511
|
+
usedManifestIds.add(chap.id);
|
|
512
|
+
if (chap.smilFilename) usedManifestIds.add(`${chap.id}-overlay`);
|
|
513
|
+
}
|
|
480
514
|
if (audioFiles) {
|
|
481
515
|
const usedAudioNames = /* @__PURE__ */ new Set();
|
|
482
516
|
for (const af of audioFiles) {
|
|
483
517
|
if (usedAudioNames.has(af.filename)) continue;
|
|
484
518
|
usedAudioNames.add(af.filename);
|
|
485
|
-
const audioId =
|
|
519
|
+
const audioId = uniqueManifestId("audio", af.filename, usedManifestIds);
|
|
486
520
|
manifestItems.push(
|
|
487
521
|
` <item id="${audioId}" href="audio/${escapeXml(af.filename)}" media-type="${af.mime}"/>`
|
|
488
522
|
);
|
|
@@ -492,7 +526,7 @@ function generateContentOpf(params) {
|
|
|
492
526
|
for (const [, img] of images) {
|
|
493
527
|
if (usedFilenames.has(img.filename)) continue;
|
|
494
528
|
usedFilenames.add(img.filename);
|
|
495
|
-
const imgId =
|
|
529
|
+
const imgId = uniqueManifestId("img", img.filename, usedManifestIds);
|
|
496
530
|
manifestItems.push(
|
|
497
531
|
` <item id="${imgId}" href="images/${escapeXml(img.filename)}" media-type="${img.mime}"/>`
|
|
498
532
|
);
|
|
@@ -676,4 +710,3 @@ export {
|
|
|
676
710
|
markdownDocToEpub,
|
|
677
711
|
docToEpub
|
|
678
712
|
};
|
|
679
|
-
//# sourceMappingURL=chunk-4VUWTSGM.js.map
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import {
|
|
2
|
+
extractPlainText
|
|
3
|
+
} from "./chunk-SBUW7NHR.js";
|
|
4
|
+
import {
|
|
5
|
+
createPackage
|
|
6
|
+
} from "./chunk-4V3KCHAP.js";
|
|
7
|
+
import {
|
|
8
|
+
escapeXml,
|
|
9
|
+
xmlDeclaration
|
|
10
|
+
} from "./chunk-JU2RHXUB.js";
|
|
11
|
+
import {
|
|
12
|
+
CONTENT_TYPE_XLSX_STYLES,
|
|
13
|
+
CONTENT_TYPE_XLSX_WORKBOOK,
|
|
14
|
+
CONTENT_TYPE_XLSX_WORKSHEET,
|
|
15
|
+
NS_R,
|
|
16
|
+
NS_SML,
|
|
17
|
+
REL_OFFICE_DOCUMENT,
|
|
18
|
+
REL_STYLES,
|
|
19
|
+
REL_WORKSHEET,
|
|
20
|
+
baseDirOf,
|
|
21
|
+
getPartRelationships,
|
|
22
|
+
getPartXml,
|
|
23
|
+
openPackage,
|
|
24
|
+
requireMainPartPath
|
|
25
|
+
} from "./chunk-X2DEAXNK.js";
|
|
26
|
+
|
|
27
|
+
// src/xlsx/index.ts
|
|
28
|
+
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
29
|
+
|
|
30
|
+
// src/xlsx/import.ts
|
|
31
|
+
var XLSX_MAIN_PART = "xl/workbook.xml";
|
|
32
|
+
function attrNS(el, ns, local, fallback) {
|
|
33
|
+
return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
|
|
34
|
+
}
|
|
35
|
+
function resolveTarget(baseDir, target) {
|
|
36
|
+
if (target.startsWith("/")) return target.replace(/^\//, "");
|
|
37
|
+
const stack = baseDir ? baseDir.split("/") : [];
|
|
38
|
+
for (const seg of target.split("/")) {
|
|
39
|
+
if (seg === "..") stack.pop();
|
|
40
|
+
else if (seg !== ".") stack.push(seg);
|
|
41
|
+
}
|
|
42
|
+
return stack.join("/");
|
|
43
|
+
}
|
|
44
|
+
async function readWorkbook(pkg, mainPart) {
|
|
45
|
+
const wb = await getPartXml(pkg, mainPart);
|
|
46
|
+
if (!wb) {
|
|
47
|
+
throw new Error(`Invalid XLSX package: workbook part "${mainPart}" could not be parsed.`);
|
|
48
|
+
}
|
|
49
|
+
const rels = await getPartRelationships(pkg, mainPart);
|
|
50
|
+
const relById = new Map(rels.map((r) => [r.id, r.target]));
|
|
51
|
+
const out = [];
|
|
52
|
+
const sheetEls = wb.getElementsByTagNameNS(NS_SML, "sheet");
|
|
53
|
+
for (let i = 0; i < sheetEls.length; i++) {
|
|
54
|
+
const el = sheetEls[i];
|
|
55
|
+
const name = el.getAttribute("name") ?? `Sheet${i + 1}`;
|
|
56
|
+
const rid = attrNS(el, NS_R, "id", "r:id");
|
|
57
|
+
const target = rid ? relById.get(rid) : void 0;
|
|
58
|
+
if (target) out.push({ name, path: resolveTarget(baseDirOf(mainPart), target) });
|
|
59
|
+
}
|
|
60
|
+
const workbookPr = wb.getElementsByTagNameNS(NS_SML, "workbookPr")[0];
|
|
61
|
+
const date1904Value = workbookPr?.getAttribute("date1904");
|
|
62
|
+
return {
|
|
63
|
+
sheets: out,
|
|
64
|
+
date1904: date1904Value === "1" || date1904Value === "true"
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function isPhonetic(el, root) {
|
|
68
|
+
for (let node = el.parentNode; node && node !== root; node = node.parentNode) {
|
|
69
|
+
if (node.nodeType !== 1) continue;
|
|
70
|
+
const parent = node;
|
|
71
|
+
if (parent.localName === "rPh" && parent.namespaceURI === NS_SML) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
function stringItemText(root) {
|
|
76
|
+
const tEls = root.getElementsByTagNameNS(NS_SML, "t");
|
|
77
|
+
let out = "";
|
|
78
|
+
for (let i = 0; i < tEls.length; i++) {
|
|
79
|
+
const t = tEls[i];
|
|
80
|
+
if (isPhonetic(t, root)) continue;
|
|
81
|
+
out += t.textContent ?? "";
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
async function readSharedStrings(pkg) {
|
|
86
|
+
const doc = await getPartXml(pkg, "xl/sharedStrings.xml");
|
|
87
|
+
if (!doc) return [];
|
|
88
|
+
const siEls = doc.getElementsByTagNameNS(NS_SML, "si");
|
|
89
|
+
const out = [];
|
|
90
|
+
for (let i = 0; i < siEls.length; i++) out.push(stringItemText(siEls[i]));
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
var BUILTIN_NUMBER_FORMATS = Object.freeze({
|
|
94
|
+
9: "0%",
|
|
95
|
+
10: "0.00%",
|
|
96
|
+
14: "mm-dd-yy",
|
|
97
|
+
15: "d-mmm-yy",
|
|
98
|
+
16: "d-mmm",
|
|
99
|
+
17: "mmm-yy",
|
|
100
|
+
18: "h:mm AM/PM",
|
|
101
|
+
19: "h:mm:ss AM/PM",
|
|
102
|
+
20: "h:mm",
|
|
103
|
+
21: "h:mm:ss",
|
|
104
|
+
22: "m/d/yy h:mm",
|
|
105
|
+
45: "mm:ss",
|
|
106
|
+
46: "[h]:mm:ss",
|
|
107
|
+
47: "mmss.0"
|
|
108
|
+
});
|
|
109
|
+
async function readCellStyles(pkg) {
|
|
110
|
+
const doc = await getPartXml(pkg, "xl/styles.xml");
|
|
111
|
+
if (!doc) return [];
|
|
112
|
+
const custom = /* @__PURE__ */ new Map();
|
|
113
|
+
const numFmtEls = doc.getElementsByTagNameNS(NS_SML, "numFmt");
|
|
114
|
+
for (let i = 0; i < numFmtEls.length; i++) {
|
|
115
|
+
const el = numFmtEls[i];
|
|
116
|
+
const id = Number.parseInt(el.getAttribute("numFmtId") ?? "", 10);
|
|
117
|
+
const code = el.getAttribute("formatCode");
|
|
118
|
+
if (Number.isFinite(id) && code) custom.set(id, code);
|
|
119
|
+
}
|
|
120
|
+
const cellXfs = doc.getElementsByTagNameNS(NS_SML, "cellXfs")[0];
|
|
121
|
+
if (!cellXfs) return [];
|
|
122
|
+
const xfEls = cellXfs.getElementsByTagNameNS(NS_SML, "xf");
|
|
123
|
+
const out = [];
|
|
124
|
+
for (let i = 0; i < xfEls.length; i++) {
|
|
125
|
+
const numFmtId = Number.parseInt(xfEls[i].getAttribute("numFmtId") ?? "0", 10);
|
|
126
|
+
out.push({
|
|
127
|
+
numFmtId,
|
|
128
|
+
formatCode: custom.get(numFmtId) ?? BUILTIN_NUMBER_FORMATS[numFmtId] ?? "General"
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return out;
|
|
132
|
+
}
|
|
133
|
+
function colIndex(ref) {
|
|
134
|
+
const m = /^([A-Za-z]+)/.exec(ref);
|
|
135
|
+
if (!m) return 0;
|
|
136
|
+
let n = 0;
|
|
137
|
+
for (const ch of m[1].toUpperCase()) n = n * 26 + (ch.charCodeAt(0) - 64);
|
|
138
|
+
return n - 1;
|
|
139
|
+
}
|
|
140
|
+
function normalizeFormatCode(formatCode) {
|
|
141
|
+
return formatCode.split(";", 1)[0].replace(/"(?:[^"]|"")*"/g, "").replace(/\\./g, "").replace(/[_*]./g, "").replace(/\[(?!h+\]|m+\]|s+\])[^\]]*\]/gi, "").toLowerCase();
|
|
142
|
+
}
|
|
143
|
+
function numberFormatKind(formatCode) {
|
|
144
|
+
const normalized = normalizeFormatCode(formatCode);
|
|
145
|
+
if (normalized === "general") return "general";
|
|
146
|
+
if (normalized.includes("%")) return "percent";
|
|
147
|
+
if (/^0+$/.test(normalized)) return "zero-pad";
|
|
148
|
+
const hasDate = /[yd]/.test(normalized);
|
|
149
|
+
const hasTime = /[hs]|\[[hms]+\]/.test(normalized);
|
|
150
|
+
if (hasDate && hasTime) return "datetime";
|
|
151
|
+
if (hasDate) return "date";
|
|
152
|
+
if (hasTime) return "time";
|
|
153
|
+
return "general";
|
|
154
|
+
}
|
|
155
|
+
function twoDigits(value) {
|
|
156
|
+
return String(value).padStart(2, "0");
|
|
157
|
+
}
|
|
158
|
+
function excelDateText(serial, date1904) {
|
|
159
|
+
const wholeDays = Math.floor(serial);
|
|
160
|
+
if (!date1904 && wholeDays === 60) return "1900-02-29";
|
|
161
|
+
const epoch = date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 31);
|
|
162
|
+
const adjustedDays = !date1904 && wholeDays > 60 ? wholeDays - 1 : wholeDays;
|
|
163
|
+
const date = new Date(epoch + adjustedDays * 864e5);
|
|
164
|
+
if (!Number.isFinite(date.getTime())) return null;
|
|
165
|
+
return `${date.getUTCFullYear()}-${twoDigits(date.getUTCMonth() + 1)}-${twoDigits(date.getUTCDate())}`;
|
|
166
|
+
}
|
|
167
|
+
function excelTimeText(serial, includeSeconds, elapsedHours) {
|
|
168
|
+
const totalSeconds = Math.round((serial - Math.floor(serial)) * 86400);
|
|
169
|
+
const hours = elapsedHours ? Math.floor(serial * 24) : Math.floor(totalSeconds / 3600) % 24;
|
|
170
|
+
const minutes = Math.floor(totalSeconds / 60) % 60;
|
|
171
|
+
const seconds = totalSeconds % 60;
|
|
172
|
+
return `${twoDigits(hours)}:${twoDigits(minutes)}${includeSeconds ? `:${twoDigits(seconds)}` : ""}`;
|
|
173
|
+
}
|
|
174
|
+
function formattedNumberText(raw, style, date1904) {
|
|
175
|
+
if (!style) return raw;
|
|
176
|
+
const value = Number(raw);
|
|
177
|
+
if (!Number.isFinite(value)) return raw;
|
|
178
|
+
const normalized = normalizeFormatCode(style.formatCode);
|
|
179
|
+
const kind = numberFormatKind(style.formatCode);
|
|
180
|
+
if (kind === "date") return excelDateText(value, date1904) ?? raw;
|
|
181
|
+
if (kind === "time") {
|
|
182
|
+
return excelTimeText(value, /s/.test(normalized), /\[[h]+\]/.test(normalized));
|
|
183
|
+
}
|
|
184
|
+
if (kind === "datetime") {
|
|
185
|
+
const date = excelDateText(value, date1904);
|
|
186
|
+
return date ? `${date} ${excelTimeText(value, /s/.test(normalized), false)}` : raw;
|
|
187
|
+
}
|
|
188
|
+
if (kind === "percent") {
|
|
189
|
+
const decimals = /0\.(0+)%/.exec(normalized)?.[1]?.length ?? 0;
|
|
190
|
+
return `${(value * 100).toFixed(decimals)}%`;
|
|
191
|
+
}
|
|
192
|
+
if (kind === "zero-pad" && Number.isInteger(value)) {
|
|
193
|
+
const width = normalized.length;
|
|
194
|
+
const sign = value < 0 ? "-" : "";
|
|
195
|
+
return `${sign}${String(Math.abs(value)).padStart(width, "0")}`;
|
|
196
|
+
}
|
|
197
|
+
return raw;
|
|
198
|
+
}
|
|
199
|
+
function cellText(cell, shared, styles, date1904) {
|
|
200
|
+
const t = cell.getAttribute("t");
|
|
201
|
+
if (t === "inlineStr") {
|
|
202
|
+
const is = cell.getElementsByTagNameNS(NS_SML, "is")[0];
|
|
203
|
+
return is ? stringItemText(is) : "";
|
|
204
|
+
}
|
|
205
|
+
const vEls = cell.getElementsByTagNameNS(NS_SML, "v");
|
|
206
|
+
const v = vEls.length ? vEls[0].textContent ?? "" : "";
|
|
207
|
+
if (t === "s") return shared[Number.parseInt(v, 10)] ?? "";
|
|
208
|
+
if (t === "b") return v === "1" ? "TRUE" : "FALSE";
|
|
209
|
+
const styleIndex = Number.parseInt(cell.getAttribute("s") ?? "0", 10);
|
|
210
|
+
return formattedNumberText(v, styles[styleIndex], date1904);
|
|
211
|
+
}
|
|
212
|
+
async function sheetToGrid(pkg, path, shared, styles, date1904) {
|
|
213
|
+
const doc = await getPartXml(pkg, path);
|
|
214
|
+
if (!doc) return [];
|
|
215
|
+
const rowEls = doc.getElementsByTagNameNS(NS_SML, "row");
|
|
216
|
+
const byIndex = /* @__PURE__ */ new Map();
|
|
217
|
+
let maxContentIdx = -1;
|
|
218
|
+
let fallbackIdx = 0;
|
|
219
|
+
for (let r = 0; r < rowEls.length; r++) {
|
|
220
|
+
const rowEl = rowEls[r];
|
|
221
|
+
const cells = rowEl.getElementsByTagNameNS(NS_SML, "c");
|
|
222
|
+
const rowArr = [];
|
|
223
|
+
for (let c = 0; c < cells.length; c++) {
|
|
224
|
+
const cell = cells[c];
|
|
225
|
+
const ref = cell.getAttribute("r");
|
|
226
|
+
const idx = ref ? colIndex(ref) : rowArr.length;
|
|
227
|
+
while (rowArr.length < idx) rowArr.push("");
|
|
228
|
+
rowArr[idx] = cellText(cell, shared, styles, date1904);
|
|
229
|
+
}
|
|
230
|
+
const rowRef = Number.parseInt(rowEl.getAttribute("r") ?? "", 10);
|
|
231
|
+
const rowIdx = Number.isFinite(rowRef) && rowRef > 0 ? rowRef - 1 : fallbackIdx;
|
|
232
|
+
fallbackIdx = rowIdx + 1;
|
|
233
|
+
byIndex.set(rowIdx, rowArr);
|
|
234
|
+
if (rowArr.some((v) => v !== "")) maxContentIdx = Math.max(maxContentIdx, rowIdx);
|
|
235
|
+
}
|
|
236
|
+
const grid = [];
|
|
237
|
+
for (let i = 0; i <= maxContentIdx; i++) grid.push(byIndex.get(i) ?? []);
|
|
238
|
+
return grid;
|
|
239
|
+
}
|
|
240
|
+
function gridToTable(grid) {
|
|
241
|
+
const maxCols = grid.reduce((m, r) => Math.max(m, r.length), 1);
|
|
242
|
+
const rows = grid.map((cells, rowIdx) => {
|
|
243
|
+
const children = [];
|
|
244
|
+
for (let c = 0; c < maxCols; c++) {
|
|
245
|
+
const value = cells[c] ?? "";
|
|
246
|
+
children.push({
|
|
247
|
+
type: "tableCell",
|
|
248
|
+
...rowIdx === 0 ? { isHeader: true } : {},
|
|
249
|
+
children: value ? [{ type: "text", value }] : []
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return { type: "tableRow", children };
|
|
253
|
+
});
|
|
254
|
+
return { type: "table", children: rows };
|
|
255
|
+
}
|
|
256
|
+
async function xlsxToMarkdownDoc(data, options = {}) {
|
|
257
|
+
const pkg = await openPackage(data, options);
|
|
258
|
+
const mainPart = requireMainPartPath(pkg, XLSX_MAIN_PART, "XLSX");
|
|
259
|
+
const [{ sheets, date1904 }, shared, styles] = await Promise.all([
|
|
260
|
+
readWorkbook(pkg, mainPart),
|
|
261
|
+
readSharedStrings(pkg),
|
|
262
|
+
readCellStyles(pkg)
|
|
263
|
+
]);
|
|
264
|
+
let selected = sheets;
|
|
265
|
+
if (options.sheet !== void 0) {
|
|
266
|
+
const picked = typeof options.sheet === "number" ? sheets[options.sheet] : sheets.find((s) => s.name === options.sheet);
|
|
267
|
+
selected = picked ? [picked] : [];
|
|
268
|
+
}
|
|
269
|
+
const children = [];
|
|
270
|
+
const single = selected.length === 1 && options.sheet !== void 0;
|
|
271
|
+
for (const sheet of selected) {
|
|
272
|
+
const grid = await sheetToGrid(pkg, sheet.path, shared, styles, date1904);
|
|
273
|
+
if (!single) {
|
|
274
|
+
children.push({ type: "heading", depth: 1, children: [{ type: "text", value: sheet.name }] });
|
|
275
|
+
}
|
|
276
|
+
if (grid.length > 0) children.push(gridToTable(grid));
|
|
277
|
+
}
|
|
278
|
+
return { type: "document", children };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// src/xlsx/export.ts
|
|
282
|
+
import { docToMarkdown } from "@bendyline/squisq/doc";
|
|
283
|
+
var NUMERIC_RE = /^-?\d+(\.\d+)?$/;
|
|
284
|
+
function isSafeNumericCell(value) {
|
|
285
|
+
if (!NUMERIC_RE.test(value)) return false;
|
|
286
|
+
const unsigned = value.startsWith("-") ? value.slice(1) : value;
|
|
287
|
+
const [integer] = unsigned.split(".");
|
|
288
|
+
if (integer.length > 1 && integer.startsWith("0")) return false;
|
|
289
|
+
const significantDigits = unsigned.replace(".", "").replace(/^0+/, "");
|
|
290
|
+
if (significantDigits.length > 15) return false;
|
|
291
|
+
return Number.isFinite(Number(value));
|
|
292
|
+
}
|
|
293
|
+
function cleanSheetName(value) {
|
|
294
|
+
return value.replace(/[[\]:*?/\\]/g, "").trim().slice(0, 31).trim().replace(/^'+|'+$/g, "");
|
|
295
|
+
}
|
|
296
|
+
function sanitizeSheetName(raw, used, fallback) {
|
|
297
|
+
const safeFallback = cleanSheetName(fallback) || "Sheet";
|
|
298
|
+
const name = cleanSheetName(raw) || safeFallback;
|
|
299
|
+
const key = name.toLocaleLowerCase("en-US");
|
|
300
|
+
if (!used.has(key)) {
|
|
301
|
+
used.add(key);
|
|
302
|
+
return name;
|
|
303
|
+
}
|
|
304
|
+
for (let n = 2; ; n++) {
|
|
305
|
+
const suffix = String(n);
|
|
306
|
+
const base = name.slice(0, 31 - suffix.length);
|
|
307
|
+
const candidate = `${base}${suffix}`;
|
|
308
|
+
const candidateKey = candidate.toLocaleLowerCase("en-US");
|
|
309
|
+
if (!used.has(candidateKey)) {
|
|
310
|
+
used.add(candidateKey);
|
|
311
|
+
return candidate;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function tableToGrid(table) {
|
|
316
|
+
return table.children.map(
|
|
317
|
+
(row) => row.children.map((cell) => extractPlainText(cell.children))
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
function collectSheets(doc, prefix) {
|
|
321
|
+
const used = /* @__PURE__ */ new Set();
|
|
322
|
+
const sheets = [];
|
|
323
|
+
let pendingHeading = null;
|
|
324
|
+
const visit = (nodes) => {
|
|
325
|
+
for (const node of nodes) {
|
|
326
|
+
if (node.type === "heading") {
|
|
327
|
+
pendingHeading = extractPlainText(node.children);
|
|
328
|
+
} else if (node.type === "table") {
|
|
329
|
+
const fallback = `${prefix}${sheets.length + 1}`;
|
|
330
|
+
const name = sanitizeSheetName(pendingHeading ?? fallback, used, fallback);
|
|
331
|
+
sheets.push({ name, grid: tableToGrid(node) });
|
|
332
|
+
pendingHeading = null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
visit(doc.children);
|
|
337
|
+
return sheets;
|
|
338
|
+
}
|
|
339
|
+
function columnLetter(index) {
|
|
340
|
+
let n = index + 1;
|
|
341
|
+
let letters = "";
|
|
342
|
+
while (n > 0) {
|
|
343
|
+
const rem = (n - 1) % 26;
|
|
344
|
+
letters = String.fromCharCode(65 + rem) + letters;
|
|
345
|
+
n = Math.floor((n - 1) / 26);
|
|
346
|
+
}
|
|
347
|
+
return letters;
|
|
348
|
+
}
|
|
349
|
+
function cellXml(value, ref, inferNumericCells) {
|
|
350
|
+
if (inferNumericCells && isSafeNumericCell(value)) {
|
|
351
|
+
return `<c r="${ref}"><v>${escapeXml(value)}</v></c>`;
|
|
352
|
+
}
|
|
353
|
+
return `<c r="${ref}" t="inlineStr"><is><t xml:space="preserve">${escapeXml(value)}</t></is></c>`;
|
|
354
|
+
}
|
|
355
|
+
function worksheetXml(grid, inferNumericCells) {
|
|
356
|
+
const rows = [];
|
|
357
|
+
for (let r = 0; r < grid.length; r++) {
|
|
358
|
+
const cells = grid[r];
|
|
359
|
+
const cellsXml = cells.map((value, c) => cellXml(value, `${columnLetter(c)}${r + 1}`, inferNumericCells)).join("");
|
|
360
|
+
rows.push(`<row r="${r + 1}">${cellsXml}</row>`);
|
|
361
|
+
}
|
|
362
|
+
return `${xmlDeclaration()}
|
|
363
|
+
<worksheet xmlns="${NS_SML}" xmlns:r="${NS_R}"><sheetData>${rows.join("")}</sheetData></worksheet>`;
|
|
364
|
+
}
|
|
365
|
+
function workbookXml(sheets) {
|
|
366
|
+
const sheetEls = sheets.map(
|
|
367
|
+
(sheet, i) => `<sheet name="${escapeXml(sheet.name)}" sheetId="${i + 1}" r:id="rId${i + 1}"/>`
|
|
368
|
+
).join("");
|
|
369
|
+
return `${xmlDeclaration()}
|
|
370
|
+
<workbook xmlns="${NS_SML}" xmlns:r="${NS_R}"><sheets>${sheetEls}</sheets></workbook>`;
|
|
371
|
+
}
|
|
372
|
+
function stylesXml() {
|
|
373
|
+
return `${xmlDeclaration()}
|
|
374
|
+
<styleSheet xmlns="${NS_SML}"><fonts count="1"><font><sz val="11"/><name val="Calibri"/></font></fonts><fills count="1"><fill><patternFill patternType="none"/></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/></cellXfs></styleSheet>`;
|
|
375
|
+
}
|
|
376
|
+
async function markdownDocToXlsx(doc, options = {}) {
|
|
377
|
+
options.signal?.throwIfAborted();
|
|
378
|
+
const prefix = cleanSheetName(options.sheetNamePrefix ?? "Sheet") || "Sheet";
|
|
379
|
+
const inferNumericCells = options.inferNumericCells ?? false;
|
|
380
|
+
let sheets = collectSheets(doc, prefix);
|
|
381
|
+
const maxCells = options.maxCells ?? 1e5;
|
|
382
|
+
if (!Number.isSafeInteger(maxCells) || maxCells < 0) {
|
|
383
|
+
throw new RangeError("maxCells must be a non-negative safe integer");
|
|
384
|
+
}
|
|
385
|
+
const cellCount = sheets.reduce(
|
|
386
|
+
(total, sheet) => total + sheet.grid.reduce((sum, row) => sum + row.length, 0),
|
|
387
|
+
0
|
|
388
|
+
);
|
|
389
|
+
if (cellCount > maxCells) {
|
|
390
|
+
throw new RangeError(`XLSX export exceeds the ${maxCells}-cell safety limit`);
|
|
391
|
+
}
|
|
392
|
+
if (sheets.length === 0) {
|
|
393
|
+
sheets = [{ name: sanitizeSheetName(`${prefix}1`, /* @__PURE__ */ new Set(), "Sheet1"), grid: [] }];
|
|
394
|
+
}
|
|
395
|
+
const pkg = createPackage();
|
|
396
|
+
sheets.forEach((sheet, i) => {
|
|
397
|
+
if ((i & 31) === 0) options.signal?.throwIfAborted();
|
|
398
|
+
const sheetPath = `xl/worksheets/sheet${i + 1}.xml`;
|
|
399
|
+
pkg.addPart(
|
|
400
|
+
sheetPath,
|
|
401
|
+
worksheetXml(sheet.grid, inferNumericCells),
|
|
402
|
+
CONTENT_TYPE_XLSX_WORKSHEET
|
|
403
|
+
);
|
|
404
|
+
pkg.addRelationship("xl/workbook.xml", {
|
|
405
|
+
id: `rId${i + 1}`,
|
|
406
|
+
type: REL_WORKSHEET,
|
|
407
|
+
target: `worksheets/sheet${i + 1}.xml`
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
pkg.addPart("xl/styles.xml", stylesXml(), CONTENT_TYPE_XLSX_STYLES);
|
|
411
|
+
pkg.addRelationship("xl/workbook.xml", {
|
|
412
|
+
id: `rId${sheets.length + 1}`,
|
|
413
|
+
type: REL_STYLES,
|
|
414
|
+
target: "styles.xml"
|
|
415
|
+
});
|
|
416
|
+
pkg.addPart("xl/workbook.xml", workbookXml(sheets), CONTENT_TYPE_XLSX_WORKBOOK);
|
|
417
|
+
pkg.addRelationship("", {
|
|
418
|
+
id: "rId1",
|
|
419
|
+
type: REL_OFFICE_DOCUMENT,
|
|
420
|
+
target: "xl/workbook.xml"
|
|
421
|
+
});
|
|
422
|
+
if (options.title || options.author) {
|
|
423
|
+
pkg.setCoreProperties({
|
|
424
|
+
title: options.title,
|
|
425
|
+
creator: options.author,
|
|
426
|
+
created: (/* @__PURE__ */ new Date()).toISOString(),
|
|
427
|
+
modified: (/* @__PURE__ */ new Date()).toISOString()
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
return pkg.toArrayBuffer();
|
|
431
|
+
}
|
|
432
|
+
async function docToXlsx(doc, options) {
|
|
433
|
+
return markdownDocToXlsx(docToMarkdown(doc), options);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// src/xlsx/index.ts
|
|
437
|
+
async function xlsxToDoc(data, options) {
|
|
438
|
+
return markdownToDoc(await xlsxToMarkdownDoc(data, options));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
export {
|
|
442
|
+
xlsxToMarkdownDoc,
|
|
443
|
+
markdownDocToXlsx,
|
|
444
|
+
docToXlsx,
|
|
445
|
+
xlsxToDoc
|
|
446
|
+
};
|