@bendyline/squisq-formats 2.4.6 → 2.5.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/NOTICE.md +1 -1
- package/dist/{chunk-KN62QJYK.js → chunk-2LT3JL7U.js} +75 -24
- package/dist/{chunk-DNGNODJP.js → chunk-5JQ5NDRC.js} +12 -2
- package/dist/{chunk-DWNNYO5H.js → chunk-A6N6IN3I.js} +5 -1
- package/dist/{chunk-6X5XN3CZ.js → chunk-AVOZAKGP.js} +3 -0
- package/dist/chunk-BXWNU4T5.js +108 -0
- package/dist/{chunk-BGDN7BXK.js → chunk-FIOSE4BO.js} +78 -6
- package/dist/{chunk-XCV242AO.js → chunk-FMQWNPCV.js} +5 -2
- package/dist/{chunk-KT5DX2QP.js → chunk-M7XPXGXW.js} +32 -9
- package/dist/chunk-RX55T5HO.js +1342 -0
- package/dist/{chunk-N2ZN3MQN.js → chunk-T4PX33AG.js} +262 -14
- package/dist/{chunk-BVGJ54NV.js → chunk-TVUX3RUC.js} +1 -1
- package/dist/docx/index.js +4 -3
- package/dist/epub/index.js +3 -2
- package/dist/export-Boq78GMq.d.ts +270 -0
- package/dist/html/index.js +2 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +17 -12
- package/dist/outside-in/index.d.ts +2 -2
- package/dist/outside-in/index.js +2 -2
- package/dist/pdf/index.js +2 -1
- package/dist/pptx/index.js +3 -3
- package/dist/registry/index.d.ts +3 -3
- package/dist/registry/index.js +1 -1
- package/dist/{types-HDLauUoa.d.ts → types-bwP9PBSk.d.ts} +1 -1
- package/dist/xlsx/index.d.ts +2 -2
- package/dist/xlsx/index.js +8 -4
- package/package.json +3 -3
- package/dist/chunk-UIBTXX4L.js +0 -446
- package/dist/export-D9msROJS.d.ts +0 -81
package/NOTICE.md
CHANGED
|
@@ -10,7 +10,7 @@ Third-party components remain under their respective license terms.
|
|
|
10
10
|
| ---------------- | ------- | ----------------------- | ---------------------------------- |
|
|
11
11
|
| @pdf-lib/fontkit | 1.1.1 | MIT | https://github.com/Hopding/fontkit |
|
|
12
12
|
| @pdf-lib/upng | 1.0.1 | MIT | https://github.com/Hopding/upng |
|
|
13
|
-
| @xmldom/xmldom | 0.9.
|
|
13
|
+
| @xmldom/xmldom | 0.9.12 | MIT | https://github.com/xmldom/xmldom |
|
|
14
14
|
| jszip | 3.10.1 | MIT OR GPL-3.0-or-later | https://github.com/Stuk/jszip |
|
|
15
15
|
| pdf-lib | 1.17.1 | MIT | https://pdf-lib.js.org |
|
|
16
16
|
| pdfjs-dist | 4.10.38 | Apache-2.0 | https://mozilla.github.io/pdf.js |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
extractPlainText
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AVOZAKGP.js";
|
|
4
4
|
import {
|
|
5
5
|
escapeXml
|
|
6
6
|
} from "./chunk-JU2RHXUB.js";
|
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
extractFilename,
|
|
14
14
|
inferMimeType
|
|
15
15
|
} from "./chunk-6RQOV3B3.js";
|
|
16
|
+
import {
|
|
17
|
+
FootnoteIndex,
|
|
18
|
+
footnoteIds
|
|
19
|
+
} from "./chunk-BXWNU4T5.js";
|
|
16
20
|
|
|
17
21
|
// src/epub/export.ts
|
|
18
22
|
import JSZip from "jszip";
|
|
@@ -31,6 +35,10 @@ async function markdownDocToEpub(doc, options = {}) {
|
|
|
31
35
|
const uuid = createEpubUuid();
|
|
32
36
|
const embeddedIconFonts = fontAwesomeFaces(collectInlineIconFamilies(doc));
|
|
33
37
|
const chapters = splitIntoChapters(doc.children);
|
|
38
|
+
const footnoteIndex = new FootnoteIndex(doc);
|
|
39
|
+
const citedAnywhere = /* @__PURE__ */ new Set();
|
|
40
|
+
collectCitedIdentifiers(doc.children, citedAnywhere);
|
|
41
|
+
const uncitedFootnotes = footnoteIndex.ordered().filter((fn) => fn.definition && !citedAnywhere.has(fn.identifier)).map((fn) => fn.identifier);
|
|
34
42
|
const imageEntries = collectDocImages(doc.children);
|
|
35
43
|
const resolvedImages = /* @__PURE__ */ new Map();
|
|
36
44
|
const usedImageNames = /* @__PURE__ */ new Set();
|
|
@@ -148,11 +156,14 @@ async function markdownDocToEpub(doc, options = {}) {
|
|
|
148
156
|
const id = `chapter-${num}`;
|
|
149
157
|
const filename = `${id}.xhtml`;
|
|
150
158
|
const audioInfo = chapterAudio[i] ?? null;
|
|
159
|
+
const isLastChapter = i === chapters.length - 1;
|
|
151
160
|
const { xhtml, ids } = renderChapterXhtml(
|
|
152
161
|
chap.nodes,
|
|
153
162
|
title,
|
|
154
163
|
resolvedImages,
|
|
155
|
-
audioInfo !== null
|
|
164
|
+
audioInfo !== null,
|
|
165
|
+
footnoteIndex,
|
|
166
|
+
isLastChapter ? uncitedFootnotes : []
|
|
156
167
|
);
|
|
157
168
|
zip.file(`OEBPS/chapters/${filename}`, xhtml);
|
|
158
169
|
let smilFilename;
|
|
@@ -267,14 +278,43 @@ function collectDocImages(nodes) {
|
|
|
267
278
|
nodes.forEach(walkBlock);
|
|
268
279
|
return images;
|
|
269
280
|
}
|
|
270
|
-
function
|
|
281
|
+
function collectCitedIdentifiers(nodes, into) {
|
|
282
|
+
for (const raw of nodes) {
|
|
283
|
+
const node = raw;
|
|
284
|
+
if (node?.type === "footnoteReference" && node.identifier) into.add(node.identifier);
|
|
285
|
+
if (Array.isArray(node?.children)) collectCitedIdentifiers(node.children, into);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function renderFootnotesXhtml(identifiers, ctx) {
|
|
289
|
+
const index = ctx.footnotes;
|
|
290
|
+
if (!index || identifiers.length === 0) return "";
|
|
291
|
+
const seen = /* @__PURE__ */ new Set();
|
|
292
|
+
const notes = index.ordered().filter(
|
|
293
|
+
(fn) => identifiers.includes(fn.identifier) && !seen.has(fn.identifier) && seen.add(fn.identifier)
|
|
294
|
+
);
|
|
295
|
+
if (notes.length === 0) return "";
|
|
296
|
+
const items = notes.map((fn) => {
|
|
297
|
+
const { def } = footnoteIds(fn.identifier);
|
|
298
|
+
const body = fn.definition ? fn.definition.children.map((c) => blockToXhtml(c, ctx)).join("") : "";
|
|
299
|
+
return `<li id="${escapeXml(def)}" epub:type="footnote"><span class="squisq-footnote-num">${fn.number}.</span> ${body}</li>`;
|
|
300
|
+
});
|
|
301
|
+
return `
|
|
302
|
+
<aside class="squisq-footnotes" epub:type="footnotes"><hr/>
|
|
303
|
+
<ol>
|
|
304
|
+
` + items.join("\n") + `
|
|
305
|
+
</ol>
|
|
306
|
+
</aside>`;
|
|
307
|
+
}
|
|
308
|
+
function renderChapterXhtml(nodes, bookTitle, images, addIds = false, footnotes, trailingFootnotes = []) {
|
|
271
309
|
const ids = [];
|
|
272
310
|
const nextId = () => {
|
|
273
311
|
const id = `p${ids.length + 1}`;
|
|
274
312
|
ids.push(id);
|
|
275
313
|
return id;
|
|
276
314
|
};
|
|
277
|
-
const
|
|
315
|
+
const ctx = { images, footnotes, cited: /* @__PURE__ */ new Set() };
|
|
316
|
+
const rendered = nodes.map((n) => blockToXhtml(n, ctx, addIds ? nextId : void 0)).join("\n");
|
|
317
|
+
const body = rendered + renderFootnotesXhtml([...ctx.cited ?? [], ...trailingFootnotes], ctx);
|
|
278
318
|
const xhtml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
279
319
|
<!DOCTYPE html>
|
|
280
320
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops">
|
|
@@ -289,7 +329,7 @@ ${body}
|
|
|
289
329
|
</html>`;
|
|
290
330
|
return { xhtml, ids };
|
|
291
331
|
}
|
|
292
|
-
function blockToXhtml(node,
|
|
332
|
+
function blockToXhtml(node, ctx, nextId) {
|
|
293
333
|
let cached;
|
|
294
334
|
const idAttr = () => {
|
|
295
335
|
if (!nextId) return "";
|
|
@@ -299,18 +339,18 @@ function blockToXhtml(node, images, nextId) {
|
|
|
299
339
|
switch (node.type) {
|
|
300
340
|
case "heading": {
|
|
301
341
|
const tag = `h${node.depth}`;
|
|
302
|
-
return `<${tag}${idAttr()}>${inlinesToXhtml(node.children,
|
|
342
|
+
return `<${tag}${idAttr()}>${inlinesToXhtml(node.children, ctx)}</${tag}>`;
|
|
303
343
|
}
|
|
304
344
|
case "paragraph":
|
|
305
|
-
return `<p${idAttr()}>${inlinesToXhtml(node.children,
|
|
345
|
+
return `<p${idAttr()}>${inlinesToXhtml(node.children, ctx)}</p>`;
|
|
306
346
|
case "blockquote":
|
|
307
347
|
return `<blockquote${idAttr()}>
|
|
308
|
-
${node.children.map((c) => blockToXhtml(c,
|
|
348
|
+
${node.children.map((c) => blockToXhtml(c, ctx, nextId)).join("\n")}
|
|
309
349
|
</blockquote>`;
|
|
310
350
|
case "list": {
|
|
311
351
|
const tag = node.ordered ? "ol" : "ul";
|
|
312
352
|
const startAttr = node.ordered && node.start && node.start !== 1 ? ` start="${node.start}"` : "";
|
|
313
|
-
const items = node.children.map((item) => listItemToXhtml(item,
|
|
353
|
+
const items = node.children.map((item) => listItemToXhtml(item, ctx)).join("\n");
|
|
314
354
|
return `<${tag}${idAttr()}${startAttr}>
|
|
315
355
|
${items}
|
|
316
356
|
</${tag}>`;
|
|
@@ -322,7 +362,7 @@ ${items}
|
|
|
322
362
|
case "thematicBreak":
|
|
323
363
|
return `<hr${idAttr()}/>`;
|
|
324
364
|
case "table":
|
|
325
|
-
return tableToXhtml(node,
|
|
365
|
+
return tableToXhtml(node, ctx, idAttr());
|
|
326
366
|
case "htmlBlock":
|
|
327
367
|
return `<p${idAttr()}>${escapeXml(node.rawHtml.replace(/<[^>]+>/g, ""))}</p>`;
|
|
328
368
|
case "math":
|
|
@@ -331,12 +371,12 @@ ${items}
|
|
|
331
371
|
return "";
|
|
332
372
|
}
|
|
333
373
|
}
|
|
334
|
-
function listItemToXhtml(item,
|
|
335
|
-
const content = item.children.map((c) => blockToXhtml(c,
|
|
336
|
-
const unwrapped = item.children.length === 1 && item.children[0].type === "paragraph" ? inlinesToXhtml(item.children[0].children,
|
|
374
|
+
function listItemToXhtml(item, ctx) {
|
|
375
|
+
const content = item.children.map((c) => blockToXhtml(c, ctx)).join("\n");
|
|
376
|
+
const unwrapped = item.children.length === 1 && item.children[0].type === "paragraph" ? inlinesToXhtml(item.children[0].children, ctx) : content;
|
|
337
377
|
return `<li>${unwrapped}</li>`;
|
|
338
378
|
}
|
|
339
|
-
function tableToXhtml(table,
|
|
379
|
+
function tableToXhtml(table, ctx, idAttr = "") {
|
|
340
380
|
const rows = table.children;
|
|
341
381
|
if (rows.length === 0) return `<table${idAttr}></table>`;
|
|
342
382
|
const headerRow = rows[0];
|
|
@@ -345,36 +385,47 @@ function tableToXhtml(table, images, idAttr = "") {
|
|
|
345
385
|
function cellToXhtml(cell, tag, colIndex) {
|
|
346
386
|
const a = align[colIndex];
|
|
347
387
|
const style = a ? ` style="text-align: ${a}"` : "";
|
|
348
|
-
return `<${tag}${style}>${inlinesToXhtml(cell.children,
|
|
388
|
+
return `<${tag}${style}>${inlinesToXhtml(cell.children, ctx)}</${tag}>`;
|
|
349
389
|
}
|
|
350
390
|
const thead = `<thead><tr>${headerRow.children.map((c, i) => cellToXhtml(c, "th", i)).join("")}</tr></thead>`;
|
|
351
391
|
const tbody = bodyRows.length > 0 ? `<tbody>${bodyRows.map((row) => `<tr>${row.children.map((c, i) => cellToXhtml(c, "td", i)).join("")}</tr>`).join("")}</tbody>` : "";
|
|
352
392
|
return `<table${idAttr}>${thead}${tbody}</table>`;
|
|
353
393
|
}
|
|
354
|
-
function inlinesToXhtml(nodes,
|
|
355
|
-
return nodes.map((n) => inlineToXhtml(n,
|
|
394
|
+
function inlinesToXhtml(nodes, ctx) {
|
|
395
|
+
return nodes.map((n) => inlineToXhtml(n, ctx)).join("");
|
|
356
396
|
}
|
|
357
|
-
function inlineToXhtml(node,
|
|
397
|
+
function inlineToXhtml(node, ctx) {
|
|
358
398
|
switch (node.type) {
|
|
359
399
|
case "text":
|
|
360
400
|
return escapeXml(node.value);
|
|
361
401
|
case "strong":
|
|
362
|
-
return `<strong>${inlinesToXhtml(node.children,
|
|
402
|
+
return `<strong>${inlinesToXhtml(node.children, ctx)}</strong>`;
|
|
363
403
|
case "emphasis":
|
|
364
|
-
return `<em>${inlinesToXhtml(node.children,
|
|
404
|
+
return `<em>${inlinesToXhtml(node.children, ctx)}</em>`;
|
|
365
405
|
case "delete":
|
|
366
|
-
return `<del>${inlinesToXhtml(node.children,
|
|
406
|
+
return `<del>${inlinesToXhtml(node.children, ctx)}</del>`;
|
|
407
|
+
case "superscript":
|
|
408
|
+
return `<sup>${inlinesToXhtml(node.children, ctx)}</sup>`;
|
|
409
|
+
case "footnoteReference": {
|
|
410
|
+
if (!ctx.footnotes) return "";
|
|
411
|
+
const { number, occurrence } = ctx.footnotes.cite(node.identifier);
|
|
412
|
+
const { ref, def } = footnoteIds(node.identifier, occurrence);
|
|
413
|
+
ctx.cited?.add(node.identifier);
|
|
414
|
+
return `<sup class="squisq-footnote-ref" epub:type="noteref"><a href="#${escapeXml(def)}" id="${escapeXml(ref)}">${number}</a></sup>`;
|
|
415
|
+
}
|
|
416
|
+
case "subscript":
|
|
417
|
+
return `<sub>${inlinesToXhtml(node.children, ctx)}</sub>`;
|
|
367
418
|
case "inlineCode":
|
|
368
419
|
return `<code>${escapeXml(node.value)}</code>`;
|
|
369
420
|
case "link": {
|
|
370
421
|
const href = sanitizeUrl(node.url, "link");
|
|
371
|
-
if (!href) return inlinesToXhtml(node.children,
|
|
422
|
+
if (!href) return inlinesToXhtml(node.children, ctx);
|
|
372
423
|
const titleAttr = node.title ? ` title="${escapeXml(node.title)}"` : "";
|
|
373
|
-
return `<a href="${escapeXml(href)}"${titleAttr}>${inlinesToXhtml(node.children,
|
|
424
|
+
return `<a href="${escapeXml(href)}"${titleAttr}>${inlinesToXhtml(node.children, ctx)}</a>`;
|
|
374
425
|
}
|
|
375
426
|
case "image": {
|
|
376
427
|
const alt = escapeXml(node.alt ?? "");
|
|
377
|
-
const resolved = images.get(node.url);
|
|
428
|
+
const resolved = ctx.images.get(node.url);
|
|
378
429
|
const safeSrc = resolved ? `../images/${resolved.filename}` : sanitizeUrl(node.url, "media");
|
|
379
430
|
if (!safeSrc) return alt;
|
|
380
431
|
const src = escapeXml(safeSrc);
|
|
@@ -105,6 +105,8 @@ var CONTENT_NODE_TYPES = {
|
|
|
105
105
|
emphasis: true,
|
|
106
106
|
strong: true,
|
|
107
107
|
delete: true,
|
|
108
|
+
superscript: true,
|
|
109
|
+
subscript: true,
|
|
108
110
|
inlineCode: true,
|
|
109
111
|
link: true,
|
|
110
112
|
image: true,
|
|
@@ -132,6 +134,8 @@ var COMMON_SUPPORTED = [
|
|
|
132
134
|
"emphasis",
|
|
133
135
|
"strong",
|
|
134
136
|
"delete",
|
|
137
|
+
"superscript",
|
|
138
|
+
"subscript",
|
|
135
139
|
"inlineCode",
|
|
136
140
|
"link",
|
|
137
141
|
"image",
|
|
@@ -407,7 +411,10 @@ function defaultFormats() {
|
|
|
407
411
|
};
|
|
408
412
|
const xlsx = {
|
|
409
413
|
id: "xlsx",
|
|
410
|
-
|
|
414
|
+
// Not 'ignored': a `{[dataTable sheet=… anchor=…]}` annotation decides which
|
|
415
|
+
// worksheet a table lands on and at which cell, so annotations demonstrably
|
|
416
|
+
// affect the exported result.
|
|
417
|
+
templateAnnotationHandling: "preserved",
|
|
411
418
|
label: "Excel (XLSX)",
|
|
412
419
|
mimeType: MIME.xlsx,
|
|
413
420
|
extensions: [".xlsx"],
|
|
@@ -424,7 +431,10 @@ function defaultFormats() {
|
|
|
424
431
|
const warnings = omitted > 0 ? [`XLSX export is tables-only; ${omitted} non-table block(s) were omitted.`] : [];
|
|
425
432
|
const blob = await markdownDocToXlsx(markdownDoc, {
|
|
426
433
|
...optionsFor(options, "xlsx"),
|
|
427
|
-
...options.title !== void 0 ? { title: options.title } : {}
|
|
434
|
+
...options.title !== void 0 ? { title: options.title } : {},
|
|
435
|
+
// Placement problems (a bad anchor, overlapping regions) degrade rather
|
|
436
|
+
// than throw, so they have to reach the caller as conversion warnings.
|
|
437
|
+
onWarning: (message) => warnings.push(message)
|
|
428
438
|
});
|
|
429
439
|
return ok(await toBytes(blob), MIME.xlsx, warnings);
|
|
430
440
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
stripHtmlTags
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AVOZAKGP.js";
|
|
4
4
|
|
|
5
5
|
// src/ooxml/relIds.ts
|
|
6
6
|
var RelIdAllocator = class {
|
|
@@ -92,6 +92,10 @@ function inlineNodeToRuns(node, handlers, format) {
|
|
|
92
92
|
return inlineNodesToRuns(node.children, handlers, { ...format, italic: true });
|
|
93
93
|
case "delete":
|
|
94
94
|
return inlineNodesToRuns(node.children, handlers, { ...format, strike: true });
|
|
95
|
+
case "superscript":
|
|
96
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, vertAlign: "superscript" });
|
|
97
|
+
case "subscript":
|
|
98
|
+
return inlineNodesToRuns(node.children, handlers, { ...format, vertAlign: "subscript" });
|
|
95
99
|
case "inlineCode":
|
|
96
100
|
return handlers.run(node.value, { ...format, code: true });
|
|
97
101
|
case "inlineMath":
|
|
@@ -11,6 +11,8 @@ function inlineToPlainText(node) {
|
|
|
11
11
|
case "emphasis":
|
|
12
12
|
case "strong":
|
|
13
13
|
case "delete":
|
|
14
|
+
case "superscript":
|
|
15
|
+
case "subscript":
|
|
14
16
|
case "link":
|
|
15
17
|
return extractPlainText(node.children);
|
|
16
18
|
case "image":
|
|
@@ -29,5 +31,6 @@ function extractPlainText(nodes) {
|
|
|
29
31
|
|
|
30
32
|
export {
|
|
31
33
|
stripHtmlTags,
|
|
34
|
+
inlineToPlainText,
|
|
32
35
|
extractPlainText
|
|
33
36
|
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/shared/footnotes.ts
|
|
2
|
+
var FootnoteIndex = class {
|
|
3
|
+
constructor(doc) {
|
|
4
|
+
this.numbers = /* @__PURE__ */ new Map();
|
|
5
|
+
this.definitions = /* @__PURE__ */ new Map();
|
|
6
|
+
/** Citations rendered so far, per identifier — see {@link cite}. */
|
|
7
|
+
this.citations = /* @__PURE__ */ new Map();
|
|
8
|
+
collectReferences(doc.children, (id) => {
|
|
9
|
+
if (!this.numbers.has(id)) this.numbers.set(id, this.numbers.size + 1);
|
|
10
|
+
});
|
|
11
|
+
collectDefinitions(doc.children, (def) => {
|
|
12
|
+
this.definitions.set(def.identifier, def);
|
|
13
|
+
if (!this.numbers.has(def.identifier))
|
|
14
|
+
this.numbers.set(def.identifier, this.numbers.size + 1);
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/** Whether the document has any footnotes at all. */
|
|
18
|
+
get isEmpty() {
|
|
19
|
+
return this.numbers.size === 0;
|
|
20
|
+
}
|
|
21
|
+
/** The reader-facing number for an identifier (assigning one if unseen). */
|
|
22
|
+
numberFor(identifier) {
|
|
23
|
+
const existing = this.numbers.get(identifier);
|
|
24
|
+
if (existing !== void 0) return existing;
|
|
25
|
+
const next = this.numbers.size + 1;
|
|
26
|
+
this.numbers.set(identifier, next);
|
|
27
|
+
return next;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Record one citation and return how to anchor it.
|
|
31
|
+
*
|
|
32
|
+
* Prose may cite the same footnote repeatedly. Every citation needs its own
|
|
33
|
+
* element id — reusing one id would emit duplicate ids, which is invalid HTML
|
|
34
|
+
* and leaves the backlink pointing at whichever copy the browser found first.
|
|
35
|
+
* Call this exactly once per rendered reference, in document order.
|
|
36
|
+
*/
|
|
37
|
+
cite(identifier) {
|
|
38
|
+
const number = this.numberFor(identifier);
|
|
39
|
+
const occurrence = (this.citations.get(identifier) ?? 0) + 1;
|
|
40
|
+
this.citations.set(identifier, occurrence);
|
|
41
|
+
return { number, occurrence };
|
|
42
|
+
}
|
|
43
|
+
/** Every footnote in reading order. */
|
|
44
|
+
ordered() {
|
|
45
|
+
return [...this.numbers.entries()].map(([identifier, number]) => ({
|
|
46
|
+
identifier,
|
|
47
|
+
number,
|
|
48
|
+
definition: this.definitions.get(identifier),
|
|
49
|
+
citations: this.citations.get(identifier) ?? 0
|
|
50
|
+
})).sort((a, b) => a.number - b.number);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function isFootnoteDefinition(node) {
|
|
54
|
+
return node.type === "footnoteDefinition";
|
|
55
|
+
}
|
|
56
|
+
function childrenOf(node) {
|
|
57
|
+
return "children" in node && Array.isArray(node.children) ? node.children : [];
|
|
58
|
+
}
|
|
59
|
+
function collectReferences(nodes, visit) {
|
|
60
|
+
for (const node of nodes) {
|
|
61
|
+
if (node.type === "footnoteDefinition") continue;
|
|
62
|
+
if (node.type === "footnoteReference") {
|
|
63
|
+
visit(node.identifier);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
collectReferences(childrenOf(node), visit);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function collectDefinitions(nodes, visit) {
|
|
70
|
+
for (const node of nodes) {
|
|
71
|
+
if (isFootnoteDefinition(node)) {
|
|
72
|
+
visit(node);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
collectDefinitions(childrenOf(node), visit);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function footnoteIds(identifier, occurrence = 1) {
|
|
79
|
+
const safe = identifier.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
80
|
+
const suffix = occurrence > 1 ? `-${occurrence}` : "";
|
|
81
|
+
return { ref: `fnref-${safe}${suffix}`, def: `fn-${safe}` };
|
|
82
|
+
}
|
|
83
|
+
function renumberFootnotes(doc) {
|
|
84
|
+
const index = new FootnoteIndex(doc);
|
|
85
|
+
if (index.isEmpty) return;
|
|
86
|
+
const renamed = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const fn of index.ordered()) renamed.set(fn.identifier, String(fn.number));
|
|
88
|
+
if ([...renamed].every(([from, to]) => from === to)) return;
|
|
89
|
+
const walk = (nodes) => {
|
|
90
|
+
for (const node of nodes) {
|
|
91
|
+
if (node.type === "footnoteReference" || node.type === "footnoteDefinition") {
|
|
92
|
+
const next = renamed.get(node.identifier);
|
|
93
|
+
if (next !== void 0) {
|
|
94
|
+
node.identifier = next;
|
|
95
|
+
if ("label" in node && node.label !== void 0) node.label = next;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if ("children" in node && Array.isArray(node.children)) walk(node.children);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
walk(doc.children);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export {
|
|
105
|
+
FootnoteIndex,
|
|
106
|
+
footnoteIds,
|
|
107
|
+
renumberFootnotes
|
|
108
|
+
};
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
inlineNodesToRuns,
|
|
5
5
|
normalizeOoxmlHex,
|
|
6
6
|
sanitizeOfficeHyperlink
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-A6N6IN3I.js";
|
|
8
8
|
import {
|
|
9
9
|
stripHtmlTags
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-AVOZAKGP.js";
|
|
11
11
|
import {
|
|
12
12
|
createPackage
|
|
13
13
|
} from "./chunk-ILCJ3WFD.js";
|
|
@@ -59,6 +59,9 @@ import {
|
|
|
59
59
|
import {
|
|
60
60
|
extToMime
|
|
61
61
|
} from "./chunk-AONELFLA.js";
|
|
62
|
+
import {
|
|
63
|
+
renumberFootnotes
|
|
64
|
+
} from "./chunk-BXWNU4T5.js";
|
|
62
65
|
|
|
63
66
|
// src/docx/export.ts
|
|
64
67
|
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
@@ -471,6 +474,7 @@ function makeRun(text, format) {
|
|
|
471
474
|
if (format.strike) rPrParts.push("<w:strike/>");
|
|
472
475
|
if (format.color) rPrParts.push(`<w:color w:val="${format.color}"/>`);
|
|
473
476
|
if (format.code) rPrParts.push(`<w:sz w:val="${DEFAULT_CODE_FONT_SIZE}"/>`);
|
|
477
|
+
if (format.vertAlign) rPrParts.push(`<w:vertAlign w:val="${format.vertAlign}"/>`);
|
|
474
478
|
const rPr = rPrParts.length > 0 ? `<w:rPr>${rPrParts.join("")}</w:rPr>` : "";
|
|
475
479
|
return `<w:r>${rPr}<w:t xml:space="preserve">${escapeXml(text)}</w:t></w:r>`;
|
|
476
480
|
}
|
|
@@ -797,7 +801,9 @@ async function docxToMarkdownDoc(data, options = {}) {
|
|
|
797
801
|
return { type: "document", children: [] };
|
|
798
802
|
}
|
|
799
803
|
const blocks = await convertDocumentStories(body, ctx);
|
|
800
|
-
|
|
804
|
+
const doc = { type: "document", children: blocks };
|
|
805
|
+
renumberFootnotes(doc);
|
|
806
|
+
return doc;
|
|
801
807
|
}
|
|
802
808
|
async function docxToDoc(data, options = {}) {
|
|
803
809
|
const markdownDoc = await docxToMarkdownDoc(data, options);
|
|
@@ -1062,7 +1068,9 @@ async function convertParagraph2(el, ctx) {
|
|
|
1062
1068
|
return { type: "paragraph", children: inlines };
|
|
1063
1069
|
}
|
|
1064
1070
|
async function convertRuns(paragraphEl, ctx) {
|
|
1065
|
-
return
|
|
1071
|
+
return normalizeParagraphWhitespace(
|
|
1072
|
+
await convertInlineElements(Array.from(paragraphEl.children), ctx)
|
|
1073
|
+
);
|
|
1066
1074
|
}
|
|
1067
1075
|
async function convertInlineElements(children, ctx) {
|
|
1068
1076
|
const result = [];
|
|
@@ -1105,6 +1113,9 @@ async function convertRun(runEl, ctx) {
|
|
|
1105
1113
|
result.push({ type: "inlineCode", value: text });
|
|
1106
1114
|
} else {
|
|
1107
1115
|
let node = { type: "text", value: text };
|
|
1116
|
+
if (format.vertAlign) {
|
|
1117
|
+
node = format.vertAlign === "superscript" ? { type: "superscript", children: [node] } : { type: "subscript", children: [node] };
|
|
1118
|
+
}
|
|
1108
1119
|
if (format.strike) {
|
|
1109
1120
|
node = { type: "delete", children: [node] };
|
|
1110
1121
|
}
|
|
@@ -1202,7 +1213,7 @@ function appendInlineGroup(target, group) {
|
|
|
1202
1213
|
target.push(...group);
|
|
1203
1214
|
}
|
|
1204
1215
|
function parseRunFormat(rPr, ctx) {
|
|
1205
|
-
if (!rPr) return { bold: false, italic: false, strike: false, code: false };
|
|
1216
|
+
if (!rPr) return { bold: false, italic: false, strike: false, code: false, vertAlign: null };
|
|
1206
1217
|
const bold = hasChildElement(rPr, "b") && !isFalseToggle(getFirstChildElement(rPr, "b"));
|
|
1207
1218
|
const italic = hasChildElement(rPr, "i") && !isFalseToggle(getFirstChildElement(rPr, "i"));
|
|
1208
1219
|
const strike = hasChildElement(rPr, "strike") && !isFalseToggle(getFirstChildElement(rPr, "strike"));
|
|
@@ -1212,7 +1223,10 @@ function parseRunFormat(rPr, ctx) {
|
|
|
1212
1223
|
const rFonts = getFirstChildElement(rPr, "rFonts");
|
|
1213
1224
|
const fontName = rFonts ? getAttr(rFonts, "ascii") ?? getAttr(rFonts, "hAnsi") ?? "" : "";
|
|
1214
1225
|
const isMonospace = /consolas|courier|mono/i.test(fontName);
|
|
1215
|
-
|
|
1226
|
+
const vertAlignEl = getFirstChildElement(rPr, "vertAlign");
|
|
1227
|
+
const vertAlignVal = vertAlignEl ? getAttr(vertAlignEl, "val") : null;
|
|
1228
|
+
const vertAlign = vertAlignVal === "superscript" || vertAlignVal === "subscript" ? vertAlignVal : null;
|
|
1229
|
+
return { bold, italic, strike, code: isCodeStyle || isMonospace, vertAlign };
|
|
1216
1230
|
}
|
|
1217
1231
|
function isFalseToggle(el) {
|
|
1218
1232
|
const val = getAttr(el, "val");
|
|
@@ -1486,6 +1500,64 @@ function mergeAdjacentText(nodes) {
|
|
|
1486
1500
|
}
|
|
1487
1501
|
return result;
|
|
1488
1502
|
}
|
|
1503
|
+
function isImportedInlineContainer(node) {
|
|
1504
|
+
return node.type === "emphasis" || node.type === "strong" || node.type === "delete" || node.type === "superscript" || node.type === "subscript" || node.type === "link";
|
|
1505
|
+
}
|
|
1506
|
+
function normalizeParagraphWhitespace(nodes) {
|
|
1507
|
+
const result = [];
|
|
1508
|
+
let line = [];
|
|
1509
|
+
const flushLine = () => {
|
|
1510
|
+
const normalized = liftFormattedBoundaryWhitespace(line);
|
|
1511
|
+
takeLeadingHorizontalWhitespace(normalized);
|
|
1512
|
+
takeTrailingHorizontalWhitespace(normalized);
|
|
1513
|
+
result.push(...mergeAdjacentText(normalized));
|
|
1514
|
+
line = [];
|
|
1515
|
+
};
|
|
1516
|
+
for (const node of nodes) {
|
|
1517
|
+
if (node.type === "break") {
|
|
1518
|
+
flushLine();
|
|
1519
|
+
result.push(node);
|
|
1520
|
+
} else {
|
|
1521
|
+
line.push(node);
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
flushLine();
|
|
1525
|
+
return result;
|
|
1526
|
+
}
|
|
1527
|
+
function liftFormattedBoundaryWhitespace(nodes) {
|
|
1528
|
+
const result = [];
|
|
1529
|
+
for (const node of nodes) {
|
|
1530
|
+
if (!isImportedInlineContainer(node)) {
|
|
1531
|
+
result.push(node);
|
|
1532
|
+
continue;
|
|
1533
|
+
}
|
|
1534
|
+
const children = liftFormattedBoundaryWhitespace(node.children);
|
|
1535
|
+
const leading = takeLeadingHorizontalWhitespace(children);
|
|
1536
|
+
const trailing = takeTrailingHorizontalWhitespace(children);
|
|
1537
|
+
if (leading) result.push({ type: "text", value: leading });
|
|
1538
|
+
if (children.length > 0) result.push({ ...node, children });
|
|
1539
|
+
if (trailing) result.push({ type: "text", value: trailing });
|
|
1540
|
+
}
|
|
1541
|
+
return mergeAdjacentText(result);
|
|
1542
|
+
}
|
|
1543
|
+
function takeLeadingHorizontalWhitespace(nodes) {
|
|
1544
|
+
const first = nodes[0];
|
|
1545
|
+
if (first?.type !== "text") return "";
|
|
1546
|
+
const match = /^[\t ]+/.exec(first.value);
|
|
1547
|
+
if (!match) return "";
|
|
1548
|
+
first.value = first.value.slice(match[0].length);
|
|
1549
|
+
if (!first.value) nodes.shift();
|
|
1550
|
+
return match[0];
|
|
1551
|
+
}
|
|
1552
|
+
function takeTrailingHorizontalWhitespace(nodes) {
|
|
1553
|
+
const last = nodes[nodes.length - 1];
|
|
1554
|
+
if (last?.type !== "text") return "";
|
|
1555
|
+
const match = /[\t ]+$/.exec(last.value);
|
|
1556
|
+
if (!match) return "";
|
|
1557
|
+
last.value = last.value.slice(0, -match[0].length);
|
|
1558
|
+
if (!last.value) nodes.pop();
|
|
1559
|
+
return match[0];
|
|
1560
|
+
}
|
|
1489
1561
|
|
|
1490
1562
|
export {
|
|
1491
1563
|
markdownDocToDocx,
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
inlineNodesToRuns,
|
|
5
5
|
sanitizeOfficeHyperlink,
|
|
6
6
|
toOoxmlHex
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-A6N6IN3I.js";
|
|
8
8
|
import {
|
|
9
9
|
extractPlainText,
|
|
10
10
|
stripHtmlTags
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-AVOZAKGP.js";
|
|
12
12
|
import {
|
|
13
13
|
createPackage
|
|
14
14
|
} from "./chunk-ILCJ3WFD.js";
|
|
@@ -1232,6 +1232,9 @@ function makeRun(text, format, style) {
|
|
|
1232
1232
|
if (format.bold) rPrParts.push(`b="1"`);
|
|
1233
1233
|
if (format.italic) rPrParts.push(`i="1"`);
|
|
1234
1234
|
if (format.strike) rPrParts.push(`strike="sngStrike"`);
|
|
1235
|
+
if (format.vertAlign) {
|
|
1236
|
+
rPrParts.push(`baseline="${format.vertAlign === "superscript" ? 3e4 : -25e3}"`);
|
|
1237
|
+
}
|
|
1235
1238
|
let innerParts = "";
|
|
1236
1239
|
if (format.code) {
|
|
1237
1240
|
innerParts += `<a:solidFill><a:srgbClr val="${style.codeColor}"/></a:solidFill>`;
|