epub-codec 0.0.0 → 1.0.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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/dist/codec.cjs +28 -0
  4. package/dist/codec.d.cts +2344 -0
  5. package/dist/codec.d.ts +2344 -0
  6. package/dist/codec.js +25 -0
  7. package/dist/diagnostics-Ee4kG7mw.d.cts +60 -0
  8. package/dist/diagnostics-Ee4kG7mw.d.ts +60 -0
  9. package/dist/diagnostics.cjs +96 -0
  10. package/dist/diagnostics.d.cts +2 -0
  11. package/dist/diagnostics.d.ts +2 -0
  12. package/dist/diagnostics.js +85 -0
  13. package/dist/format.cjs +9 -0
  14. package/dist/format.d.cts +6 -0
  15. package/dist/format.d.ts +6 -0
  16. package/dist/format.js +6 -0
  17. package/dist/image/dimensions.cjs +82 -0
  18. package/dist/image/dimensions.d.cts +11 -0
  19. package/dist/image/dimensions.d.ts +11 -0
  20. package/dist/image/dimensions.js +79 -0
  21. package/dist/index.cjs +105 -0
  22. package/dist/index.d.cts +32 -0
  23. package/dist/index.d.ts +32 -0
  24. package/dist/index.js +32 -0
  25. package/dist/nav/nav3.cjs +19 -0
  26. package/dist/nav/nav3.d.cts +4 -0
  27. package/dist/nav/nav3.d.ts +4 -0
  28. package/dist/nav/nav3.js +18 -0
  29. package/dist/nav/ncx.cjs +17 -0
  30. package/dist/nav/ncx.d.cts +4 -0
  31. package/dist/nav/ncx.d.ts +4 -0
  32. package/dist/nav/ncx.js +16 -0
  33. package/dist/nav/reconcile.cjs +8 -0
  34. package/dist/nav/reconcile.d.cts +4 -0
  35. package/dist/nav/reconcile.d.ts +4 -0
  36. package/dist/nav/reconcile.js +7 -0
  37. package/dist/nav/write.cjs +39 -0
  38. package/dist/nav/write.d.cts +10 -0
  39. package/dist/nav/write.d.ts +10 -0
  40. package/dist/nav/write.js +37 -0
  41. package/dist/ocf/container.cjs +20 -0
  42. package/dist/ocf/container.d.cts +4 -0
  43. package/dist/ocf/container.d.ts +4 -0
  44. package/dist/ocf/container.js +19 -0
  45. package/dist/ocf/write.cjs +13 -0
  46. package/dist/ocf/write.d.cts +4 -0
  47. package/dist/ocf/write.d.ts +4 -0
  48. package/dist/ocf/write.js +12 -0
  49. package/dist/opf/metadata.cjs +51 -0
  50. package/dist/opf/metadata.d.cts +7 -0
  51. package/dist/opf/metadata.d.ts +7 -0
  52. package/dist/opf/metadata.js +50 -0
  53. package/dist/opf/parse.cjs +47 -0
  54. package/dist/opf/parse.d.cts +6 -0
  55. package/dist/opf/parse.d.ts +6 -0
  56. package/dist/opf/parse.js +46 -0
  57. package/dist/opf/types.cjs +0 -0
  58. package/dist/opf/types.d.cts +20 -0
  59. package/dist/opf/types.d.ts +20 -0
  60. package/dist/opf/types.js +0 -0
  61. package/dist/opf/write.cjs +55 -0
  62. package/dist/opf/write.d.cts +12 -0
  63. package/dist/opf/write.d.ts +12 -0
  64. package/dist/opf/write.js +54 -0
  65. package/dist/path.cjs +22 -0
  66. package/dist/path.d.cts +5 -0
  67. package/dist/path.d.ts +5 -0
  68. package/dist/path.js +20 -0
  69. package/dist/read.cjs +166 -0
  70. package/dist/read.d.cts +10 -0
  71. package/dist/read.d.ts +10 -0
  72. package/dist/read.js +164 -0
  73. package/dist/util/base64.cjs +51 -0
  74. package/dist/util/base64.d.cts +5 -0
  75. package/dist/util/base64.d.ts +5 -0
  76. package/dist/util/base64.js +49 -0
  77. package/dist/write.cjs +136 -0
  78. package/dist/write.d.cts +10 -0
  79. package/dist/write.d.ts +10 -0
  80. package/dist/write.js +134 -0
  81. package/dist/xhtml/context.cjs +0 -0
  82. package/dist/xhtml/context.d.cts +20 -0
  83. package/dist/xhtml/context.d.ts +20 -0
  84. package/dist/xhtml/context.js +0 -0
  85. package/dist/xhtml/footnote.cjs +32 -0
  86. package/dist/xhtml/footnote.d.cts +7 -0
  87. package/dist/xhtml/footnote.d.ts +7 -0
  88. package/dist/xhtml/footnote.js +29 -0
  89. package/dist/xhtml/inline.cjs +128 -0
  90. package/dist/xhtml/inline.d.cts +11 -0
  91. package/dist/xhtml/inline.d.ts +11 -0
  92. package/dist/xhtml/inline.js +127 -0
  93. package/dist/xhtml/list-id.cjs +22 -0
  94. package/dist/xhtml/list-id.d.cts +13 -0
  95. package/dist/xhtml/list-id.d.ts +13 -0
  96. package/dist/xhtml/list-id.js +20 -0
  97. package/dist/xhtml/read.cjs +423 -0
  98. package/dist/xhtml/read.d.cts +20 -0
  99. package/dist/xhtml/read.d.ts +20 -0
  100. package/dist/xhtml/read.js +422 -0
  101. package/dist/xhtml/style-constants.cjs +13 -0
  102. package/dist/xhtml/style-constants.d.cts +8 -0
  103. package/dist/xhtml/style-constants.d.ts +8 -0
  104. package/dist/xhtml/style-constants.js +8 -0
  105. package/dist/xhtml/write.cjs +183 -0
  106. package/dist/xhtml/write.d.cts +12 -0
  107. package/dist/xhtml/write.d.ts +12 -0
  108. package/dist/xhtml/write.js +182 -0
  109. package/dist/xml/build.cjs +46 -0
  110. package/dist/xml/build.d.cts +5 -0
  111. package/dist/xml/build.d.ts +5 -0
  112. package/dist/xml/build.js +46 -0
  113. package/dist/xml/entities.cjs +20 -0
  114. package/dist/xml/entities.d.cts +5 -0
  115. package/dist/xml/entities.d.ts +5 -0
  116. package/dist/xml/entities.js +18 -0
  117. package/dist/xml/node.cjs +67 -0
  118. package/dist/xml/node.d.cts +83 -0
  119. package/dist/xml/node.d.ts +83 -0
  120. package/dist/xml/node.js +58 -0
  121. package/dist/xml/parse.cjs +88 -0
  122. package/dist/xml/parse.d.cts +5 -0
  123. package/dist/xml/parse.d.ts +5 -0
  124. package/dist/xml/parse.js +88 -0
  125. package/dist/xml/query.cjs +44 -0
  126. package/dist/xml/query.d.cts +11 -0
  127. package/dist/xml/query.d.ts +11 -0
  128. package/dist/xml/query.js +37 -0
  129. package/dist/zip.cjs +19 -0
  130. package/dist/zip.d.cts +9 -0
  131. package/dist/zip.d.ts +9 -0
  132. package/dist/zip.js +17 -0
  133. package/package.json +95 -2
@@ -0,0 +1,51 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/util/base64.ts
3
+ const TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4
+ const DECODE = (() => {
5
+ const map = (/* @__PURE__ */ new Uint8Array(256)).fill(255);
6
+ for (let i = 0; i < 64; i += 1) map[TABLE.charCodeAt(i)] = i;
7
+ return map;
8
+ })();
9
+ function bytesToBase64(bytes) {
10
+ let out = "";
11
+ const len = bytes.length;
12
+ for (let i = 0; i < len; i += 3) {
13
+ const b0 = bytes[i] ?? 0;
14
+ const b1 = bytes[i + 1] ?? 0;
15
+ const b2 = bytes[i + 2] ?? 0;
16
+ out += TABLE.charAt(b0 >> 2);
17
+ out += TABLE.charAt((b0 & 3) << 4 | b1 >> 4);
18
+ out += i + 1 < len ? TABLE.charAt((b1 & 15) << 2 | b2 >> 6) : "=";
19
+ out += i + 2 < len ? TABLE.charAt(b2 & 63) : "=";
20
+ }
21
+ return out;
22
+ }
23
+ function base64ToBytes(b64) {
24
+ const clean = b64.replace(/[^A-Za-z0-9+/=]/g, "");
25
+ const len = clean.length;
26
+ const out = new Uint8Array(Math.floor(len * 3 / 4));
27
+ let position = 0;
28
+ for (let i = 0; i < len; i += 4) {
29
+ const c0 = DECODE[clean.charCodeAt(i)] ?? 255;
30
+ const c1 = DECODE[clean.charCodeAt(i + 1)] ?? 255;
31
+ const code2 = clean.charCodeAt(i + 2);
32
+ const code3 = clean.charCodeAt(i + 3);
33
+ if (c0 === 255 || c1 === 255) throw new Error("invalid base64 input");
34
+ out[position] = c0 << 2 | c1 >> 4;
35
+ position += 1;
36
+ if (code2 !== 61) {
37
+ const d2 = DECODE[code2] ?? 255;
38
+ out[position] = (c1 & 15) << 4 | d2 >> 2;
39
+ position += 1;
40
+ if (code3 !== 61) {
41
+ const d3 = DECODE[code3] ?? 255;
42
+ out[position] = (d2 & 3) << 6 | d3;
43
+ position += 1;
44
+ }
45
+ }
46
+ }
47
+ return out.subarray(0, position);
48
+ }
49
+ //#endregion
50
+ exports.base64ToBytes = base64ToBytes;
51
+ exports.bytesToBase64 = bytesToBase64;
@@ -0,0 +1,5 @@
1
+ //#region src/util/base64.d.ts
2
+ declare function bytesToBase64(bytes: Uint8Array<ArrayBuffer>): string;
3
+ declare function base64ToBytes(b64: string): Uint8Array<ArrayBuffer>;
4
+ //#endregion
5
+ export { base64ToBytes, bytesToBase64 };
@@ -0,0 +1,5 @@
1
+ //#region src/util/base64.d.ts
2
+ declare function bytesToBase64(bytes: Uint8Array<ArrayBuffer>): string;
3
+ declare function base64ToBytes(b64: string): Uint8Array<ArrayBuffer>;
4
+ //#endregion
5
+ export { base64ToBytes, bytesToBase64 };
@@ -0,0 +1,49 @@
1
+ //#region src/util/base64.ts
2
+ const TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
3
+ const DECODE = (() => {
4
+ const map = (/* @__PURE__ */ new Uint8Array(256)).fill(255);
5
+ for (let i = 0; i < 64; i += 1) map[TABLE.charCodeAt(i)] = i;
6
+ return map;
7
+ })();
8
+ function bytesToBase64(bytes) {
9
+ let out = "";
10
+ const len = bytes.length;
11
+ for (let i = 0; i < len; i += 3) {
12
+ const b0 = bytes[i] ?? 0;
13
+ const b1 = bytes[i + 1] ?? 0;
14
+ const b2 = bytes[i + 2] ?? 0;
15
+ out += TABLE.charAt(b0 >> 2);
16
+ out += TABLE.charAt((b0 & 3) << 4 | b1 >> 4);
17
+ out += i + 1 < len ? TABLE.charAt((b1 & 15) << 2 | b2 >> 6) : "=";
18
+ out += i + 2 < len ? TABLE.charAt(b2 & 63) : "=";
19
+ }
20
+ return out;
21
+ }
22
+ function base64ToBytes(b64) {
23
+ const clean = b64.replace(/[^A-Za-z0-9+/=]/g, "");
24
+ const len = clean.length;
25
+ const out = new Uint8Array(Math.floor(len * 3 / 4));
26
+ let position = 0;
27
+ for (let i = 0; i < len; i += 4) {
28
+ const c0 = DECODE[clean.charCodeAt(i)] ?? 255;
29
+ const c1 = DECODE[clean.charCodeAt(i + 1)] ?? 255;
30
+ const code2 = clean.charCodeAt(i + 2);
31
+ const code3 = clean.charCodeAt(i + 3);
32
+ if (c0 === 255 || c1 === 255) throw new Error("invalid base64 input");
33
+ out[position] = c0 << 2 | c1 >> 4;
34
+ position += 1;
35
+ if (code2 !== 61) {
36
+ const d2 = DECODE[code2] ?? 255;
37
+ out[position] = (c1 & 15) << 4 | d2 >> 2;
38
+ position += 1;
39
+ if (code3 !== 61) {
40
+ const d3 = DECODE[code3] ?? 255;
41
+ out[position] = (d2 & 3) << 6 | d3;
42
+ position += 1;
43
+ }
44
+ }
45
+ }
46
+ return out.subarray(0, position);
47
+ }
48
+ //#endregion
49
+ export { base64ToBytes, bytesToBase64 };
package/dist/write.cjs ADDED
@@ -0,0 +1,136 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_diagnostics = require("./diagnostics.cjs");
3
+ const require_xml_parse = require("./xml/parse.cjs");
4
+ const require_format = require("./format.cjs");
5
+ const require_xml_entities = require("./xml/entities.cjs");
6
+ const require_xml_build = require("./xml/build.cjs");
7
+ const require_zip = require("./zip.cjs");
8
+ const require_ocf_write = require("./ocf/write.cjs");
9
+ const require_nav_write = require("./nav/write.cjs");
10
+ const require_opf_write = require("./opf/write.cjs");
11
+ const require_xhtml_write = require("./xhtml/write.cjs");
12
+ let document_schema_js = require("document-schema.js");
13
+ //#region src/write.ts
14
+ const OPF_DIR = "OEBPS";
15
+ const OPF_PATH = `${OPF_DIR}/content.opf`;
16
+ const NAV_PATH = `${OPF_DIR}/nav.xhtml`;
17
+ const XHTML_DOCTYPE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">";
18
+ function sectionXhtmlPath(index) {
19
+ return `${OPF_DIR}/section${String(index + 1)}.xhtml`;
20
+ }
21
+ function buildHead(title, residueXml) {
22
+ return {
23
+ type: "element",
24
+ tag: "head",
25
+ attributes: [],
26
+ children: [{
27
+ type: "element",
28
+ tag: "title",
29
+ attributes: [],
30
+ children: [{
31
+ type: "text",
32
+ value: require_xml_entities.encodeEntities(title)
33
+ }]
34
+ }, ...residueXml === void 0 ? [] : require_xml_parse.parseXml(residueXml)]
35
+ };
36
+ }
37
+ function imageExtension(format) {
38
+ return format === "png" ? "png" : "jpg";
39
+ }
40
+ function imageMediaType(format) {
41
+ return format === "png" ? "image/png" : "image/jpeg";
42
+ }
43
+ function writeEpubContent(document, options = {}) {
44
+ if (document.kind !== "wordprocessing") throw new require_diagnostics.EpubUnsupportedDocumentKindError(document.kind);
45
+ const sink = options.sink ?? require_diagnostics.NOOP_EPUB_DIAGNOSTIC_SINK;
46
+ const registeredImages = [];
47
+ const registerImage = (bytes, format) => {
48
+ const index = registeredImages.length + 1;
49
+ const href = `images/img${String(index)}.${imageExtension(format)}`;
50
+ registeredImages.push({
51
+ href,
52
+ bytes,
53
+ mediaType: imageMediaType(format)
54
+ });
55
+ return href;
56
+ };
57
+ const sectionXhtml = [];
58
+ document.sections.forEach((section, index) => {
59
+ const sourceHref = sectionXhtmlPath(index);
60
+ try {
61
+ const body = require_xhtml_write.writeXhtmlBody(section.blocks, {
62
+ registerImage,
63
+ sink,
64
+ sourceHref
65
+ });
66
+ const residueXml = section.source?.format === "epub" ? section.source.xml : void 0;
67
+ sectionXhtml.push({
68
+ body,
69
+ residueXml
70
+ });
71
+ } catch (error) {
72
+ if (error instanceof document_schema_js.ConstructMarkerImbalanceError) throw new require_diagnostics.EpubUnbalancedConstructMarkersError(error.imbalance.kind, error.imbalance.index);
73
+ throw error;
74
+ }
75
+ });
76
+ const navEntries = document.sections.map((section, index) => ({
77
+ href: `section${String(index + 1)}.xhtml`,
78
+ section
79
+ }));
80
+ const manifestItems = [
81
+ {
82
+ id: "nav",
83
+ href: "nav.xhtml",
84
+ mediaType: "application/xhtml+xml",
85
+ properties: ["nav"]
86
+ },
87
+ ...document.sections.map((_section, index) => ({
88
+ id: `s${String(index + 1)}`,
89
+ href: `section${String(index + 1)}.xhtml`,
90
+ mediaType: "application/xhtml+xml",
91
+ properties: []
92
+ })),
93
+ ...registeredImages.map((image, index) => ({
94
+ id: `img${String(index + 1)}`,
95
+ href: image.href,
96
+ mediaType: image.mediaType,
97
+ properties: []
98
+ }))
99
+ ];
100
+ const opfXml = require_opf_write.writeOpf({
101
+ metadata: document.metadata,
102
+ manifestItems,
103
+ spineIdrefs: document.sections.map((_section, index) => `s${String(index + 1)}`),
104
+ identifier: `urn:uuid:${crypto.randomUUID()}`
105
+ });
106
+ const entries = [
107
+ [require_format.OCF_MIMETYPE_PATH, {
108
+ bytes: new TextEncoder().encode(require_format.EPUB_MIME_TYPE),
109
+ stored: true
110
+ }],
111
+ [require_format.OCF_CONTAINER_PATH, { bytes: new TextEncoder().encode(require_ocf_write.writeContainerXml(OPF_PATH)) }],
112
+ [OPF_PATH, { bytes: new TextEncoder().encode(opfXml) }],
113
+ [NAV_PATH, { bytes: new TextEncoder().encode(require_nav_write.writeNav3Document(navEntries)) }]
114
+ ];
115
+ sectionXhtml.forEach(({ body, residueXml }, index) => {
116
+ const section = document.sections[index];
117
+ const head = buildHead(section === void 0 ? `Section ${String(index + 1)}` : require_nav_write.sectionTitle(section, index), residueXml);
118
+ const xml = `${XHTML_DOCTYPE}${require_xml_build.buildXml([head, body])}</html>`;
119
+ entries.push([sectionXhtmlPath(index), { bytes: new TextEncoder().encode(xml) }]);
120
+ });
121
+ for (const image of registeredImages) entries.push([`${OPF_DIR}/${image.href}`, { bytes: image.bytes }]);
122
+ return require_zip.zipPackage(entries);
123
+ }
124
+ function writeEpub(tree, options = {}) {
125
+ if (tree.kind !== "wordprocessing") throw new require_diagnostics.EpubUnsupportedDocumentKindError(tree.kind);
126
+ let flat;
127
+ try {
128
+ flat = (0, document_schema_js.flattenTree)(tree);
129
+ } catch (error) {
130
+ throw new require_diagnostics.EpubPackageFlattenError(error);
131
+ }
132
+ return writeEpubContent(flat, options);
133
+ }
134
+ //#endregion
135
+ exports.writeEpub = writeEpub;
136
+ exports.writeEpubContent = writeEpubContent;
@@ -0,0 +1,10 @@
1
+ import { i as EpubDiagnosticSink } from "./diagnostics-Ee4kG7mw.cjs";
2
+ import { ContentDocument, DocumentTree } from "document-schema.js";
3
+ //#region src/write.d.ts
4
+ interface WriteEpubOptions {
5
+ readonly sink?: EpubDiagnosticSink;
6
+ }
7
+ declare function writeEpubContent(document: ContentDocument, options?: WriteEpubOptions): Uint8Array<ArrayBuffer>;
8
+ declare function writeEpub(tree: DocumentTree, options?: WriteEpubOptions): Uint8Array<ArrayBuffer>;
9
+ //#endregion
10
+ export { WriteEpubOptions, writeEpub, writeEpubContent };
@@ -0,0 +1,10 @@
1
+ import { i as EpubDiagnosticSink } from "./diagnostics-Ee4kG7mw.js";
2
+ import { ContentDocument, DocumentTree } from "document-schema.js";
3
+ //#region src/write.d.ts
4
+ interface WriteEpubOptions {
5
+ readonly sink?: EpubDiagnosticSink;
6
+ }
7
+ declare function writeEpubContent(document: ContentDocument, options?: WriteEpubOptions): Uint8Array<ArrayBuffer>;
8
+ declare function writeEpub(tree: DocumentTree, options?: WriteEpubOptions): Uint8Array<ArrayBuffer>;
9
+ //#endregion
10
+ export { WriteEpubOptions, writeEpub, writeEpubContent };
package/dist/write.js ADDED
@@ -0,0 +1,134 @@
1
+ import { EpubPackageFlattenError, EpubUnbalancedConstructMarkersError, EpubUnsupportedDocumentKindError, NOOP_EPUB_DIAGNOSTIC_SINK } from "./diagnostics.js";
2
+ import { parseXml } from "./xml/parse.js";
3
+ import { EPUB_MIME_TYPE, OCF_CONTAINER_PATH, OCF_MIMETYPE_PATH } from "./format.js";
4
+ import { encodeEntities } from "./xml/entities.js";
5
+ import { buildXml } from "./xml/build.js";
6
+ import { zipPackage } from "./zip.js";
7
+ import { writeContainerXml } from "./ocf/write.js";
8
+ import { sectionTitle, writeNav3Document } from "./nav/write.js";
9
+ import { writeOpf } from "./opf/write.js";
10
+ import { writeXhtmlBody } from "./xhtml/write.js";
11
+ import { ConstructMarkerImbalanceError, flattenTree } from "document-schema.js";
12
+ //#region src/write.ts
13
+ const OPF_DIR = "OEBPS";
14
+ const OPF_PATH = `${OPF_DIR}/content.opf`;
15
+ const NAV_PATH = `${OPF_DIR}/nav.xhtml`;
16
+ const XHTML_DOCTYPE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:epub=\"http://www.idpf.org/2007/ops\">";
17
+ function sectionXhtmlPath(index) {
18
+ return `${OPF_DIR}/section${String(index + 1)}.xhtml`;
19
+ }
20
+ function buildHead(title, residueXml) {
21
+ return {
22
+ type: "element",
23
+ tag: "head",
24
+ attributes: [],
25
+ children: [{
26
+ type: "element",
27
+ tag: "title",
28
+ attributes: [],
29
+ children: [{
30
+ type: "text",
31
+ value: encodeEntities(title)
32
+ }]
33
+ }, ...residueXml === void 0 ? [] : parseXml(residueXml)]
34
+ };
35
+ }
36
+ function imageExtension(format) {
37
+ return format === "png" ? "png" : "jpg";
38
+ }
39
+ function imageMediaType(format) {
40
+ return format === "png" ? "image/png" : "image/jpeg";
41
+ }
42
+ function writeEpubContent(document, options = {}) {
43
+ if (document.kind !== "wordprocessing") throw new EpubUnsupportedDocumentKindError(document.kind);
44
+ const sink = options.sink ?? NOOP_EPUB_DIAGNOSTIC_SINK;
45
+ const registeredImages = [];
46
+ const registerImage = (bytes, format) => {
47
+ const index = registeredImages.length + 1;
48
+ const href = `images/img${String(index)}.${imageExtension(format)}`;
49
+ registeredImages.push({
50
+ href,
51
+ bytes,
52
+ mediaType: imageMediaType(format)
53
+ });
54
+ return href;
55
+ };
56
+ const sectionXhtml = [];
57
+ document.sections.forEach((section, index) => {
58
+ const sourceHref = sectionXhtmlPath(index);
59
+ try {
60
+ const body = writeXhtmlBody(section.blocks, {
61
+ registerImage,
62
+ sink,
63
+ sourceHref
64
+ });
65
+ const residueXml = section.source?.format === "epub" ? section.source.xml : void 0;
66
+ sectionXhtml.push({
67
+ body,
68
+ residueXml
69
+ });
70
+ } catch (error) {
71
+ if (error instanceof ConstructMarkerImbalanceError) throw new EpubUnbalancedConstructMarkersError(error.imbalance.kind, error.imbalance.index);
72
+ throw error;
73
+ }
74
+ });
75
+ const navEntries = document.sections.map((section, index) => ({
76
+ href: `section${String(index + 1)}.xhtml`,
77
+ section
78
+ }));
79
+ const manifestItems = [
80
+ {
81
+ id: "nav",
82
+ href: "nav.xhtml",
83
+ mediaType: "application/xhtml+xml",
84
+ properties: ["nav"]
85
+ },
86
+ ...document.sections.map((_section, index) => ({
87
+ id: `s${String(index + 1)}`,
88
+ href: `section${String(index + 1)}.xhtml`,
89
+ mediaType: "application/xhtml+xml",
90
+ properties: []
91
+ })),
92
+ ...registeredImages.map((image, index) => ({
93
+ id: `img${String(index + 1)}`,
94
+ href: image.href,
95
+ mediaType: image.mediaType,
96
+ properties: []
97
+ }))
98
+ ];
99
+ const opfXml = writeOpf({
100
+ metadata: document.metadata,
101
+ manifestItems,
102
+ spineIdrefs: document.sections.map((_section, index) => `s${String(index + 1)}`),
103
+ identifier: `urn:uuid:${crypto.randomUUID()}`
104
+ });
105
+ const entries = [
106
+ [OCF_MIMETYPE_PATH, {
107
+ bytes: new TextEncoder().encode(EPUB_MIME_TYPE),
108
+ stored: true
109
+ }],
110
+ [OCF_CONTAINER_PATH, { bytes: new TextEncoder().encode(writeContainerXml(OPF_PATH)) }],
111
+ [OPF_PATH, { bytes: new TextEncoder().encode(opfXml) }],
112
+ [NAV_PATH, { bytes: new TextEncoder().encode(writeNav3Document(navEntries)) }]
113
+ ];
114
+ sectionXhtml.forEach(({ body, residueXml }, index) => {
115
+ const section = document.sections[index];
116
+ const head = buildHead(section === void 0 ? `Section ${String(index + 1)}` : sectionTitle(section, index), residueXml);
117
+ const xml = `${XHTML_DOCTYPE}${buildXml([head, body])}</html>`;
118
+ entries.push([sectionXhtmlPath(index), { bytes: new TextEncoder().encode(xml) }]);
119
+ });
120
+ for (const image of registeredImages) entries.push([`${OPF_DIR}/${image.href}`, { bytes: image.bytes }]);
121
+ return zipPackage(entries);
122
+ }
123
+ function writeEpub(tree, options = {}) {
124
+ if (tree.kind !== "wordprocessing") throw new EpubUnsupportedDocumentKindError(tree.kind);
125
+ let flat;
126
+ try {
127
+ flat = flattenTree(tree);
128
+ } catch (error) {
129
+ throw new EpubPackageFlattenError(error);
130
+ }
131
+ return writeEpubContent(flat, options);
132
+ }
133
+ //#endregion
134
+ export { writeEpub, writeEpubContent };
File without changes
@@ -0,0 +1,20 @@
1
+ import { i as EpubDiagnosticSink } from "../diagnostics-Ee4kG7mw.cjs";
2
+ import { XmlElement } from "../xml/node.cjs";
3
+ //#region src/xhtml/context.d.ts
4
+ interface XhtmlReadContext {
5
+ readonly resolveImage: (href: string) => Uint8Array<ArrayBuffer> | undefined;
6
+ readonly sink: EpubDiagnosticSink;
7
+ readonly sourceHref: string;
8
+ readonly idElements: ReadonlyMap<string, XmlElement>;
9
+ readonly footnoteTargetIds: ReadonlySet<string>;
10
+ readonly quoteDepth: number;
11
+ }
12
+ interface InlineStyle {
13
+ readonly bold?: boolean;
14
+ readonly italic?: boolean;
15
+ readonly underline?: boolean;
16
+ readonly strike?: boolean;
17
+ readonly fontFamily?: string;
18
+ }
19
+ //#endregion
20
+ export { InlineStyle, XhtmlReadContext };
@@ -0,0 +1,20 @@
1
+ import { i as EpubDiagnosticSink } from "../diagnostics-Ee4kG7mw.js";
2
+ import { XmlElement } from "../xml/node.js";
3
+ //#region src/xhtml/context.d.ts
4
+ interface XhtmlReadContext {
5
+ readonly resolveImage: (href: string) => Uint8Array<ArrayBuffer> | undefined;
6
+ readonly sink: EpubDiagnosticSink;
7
+ readonly sourceHref: string;
8
+ readonly idElements: ReadonlyMap<string, XmlElement>;
9
+ readonly footnoteTargetIds: ReadonlySet<string>;
10
+ readonly quoteDepth: number;
11
+ }
12
+ interface InlineStyle {
13
+ readonly bold?: boolean;
14
+ readonly italic?: boolean;
15
+ readonly underline?: boolean;
16
+ readonly strike?: boolean;
17
+ readonly fontFamily?: string;
18
+ }
19
+ //#endregion
20
+ export { InlineStyle, XhtmlReadContext };
File without changes
@@ -0,0 +1,32 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_xml_query = require("../xml/query.cjs");
3
+ //#region src/xhtml/footnote.ts
4
+ const FOOTNOTE_CLASS_PATTERN = /footnote|noteref/i;
5
+ function epubTypeValues(element) {
6
+ const value = require_xml_query.attrValue(element, "epub:type");
7
+ return value === void 0 ? [] : value.split(/\s+/u).filter((v) => v.length > 0);
8
+ }
9
+ function hasFootnoteClass(element) {
10
+ const className = require_xml_query.attrValue(element, "class");
11
+ return className !== void 0 && FOOTNOTE_CLASS_PATTERN.test(className);
12
+ }
13
+ function sameDocumentFragment(href) {
14
+ if (href === void 0 || !href.startsWith("#") || href.length < 2) return;
15
+ return href.slice(1);
16
+ }
17
+ function isFootnoteReferenceAnchor(anchor, idElements) {
18
+ const fragment = sameDocumentFragment(require_xml_query.attrValue(anchor, "href"));
19
+ if (fragment === void 0) return;
20
+ const target = idElements.get(fragment);
21
+ if (target === void 0) return;
22
+ if (epubTypeValues(anchor).some((v) => /noteref/i.test(v))) return fragment;
23
+ if (hasFootnoteClass(anchor) || hasFootnoteClass(target)) return fragment;
24
+ }
25
+ function isFootnoteAside(element) {
26
+ if (element.tag !== "aside") return false;
27
+ return epubTypeValues(element).some((v) => /footnote|rearnote/i.test(v));
28
+ }
29
+ //#endregion
30
+ exports.isFootnoteAside = isFootnoteAside;
31
+ exports.isFootnoteReferenceAnchor = isFootnoteReferenceAnchor;
32
+ exports.sameDocumentFragment = sameDocumentFragment;
@@ -0,0 +1,7 @@
1
+ import { XmlElement } from "../xml/node.cjs";
2
+ //#region src/xhtml/footnote.d.ts
3
+ declare function sameDocumentFragment(href: string | undefined): string | undefined;
4
+ declare function isFootnoteReferenceAnchor(anchor: XmlElement, idElements: ReadonlyMap<string, XmlElement>): string | undefined;
5
+ declare function isFootnoteAside(element: XmlElement): boolean;
6
+ //#endregion
7
+ export { isFootnoteAside, isFootnoteReferenceAnchor, sameDocumentFragment };
@@ -0,0 +1,7 @@
1
+ import { XmlElement } from "../xml/node.js";
2
+ //#region src/xhtml/footnote.d.ts
3
+ declare function sameDocumentFragment(href: string | undefined): string | undefined;
4
+ declare function isFootnoteReferenceAnchor(anchor: XmlElement, idElements: ReadonlyMap<string, XmlElement>): string | undefined;
5
+ declare function isFootnoteAside(element: XmlElement): boolean;
6
+ //#endregion
7
+ export { isFootnoteAside, isFootnoteReferenceAnchor, sameDocumentFragment };
@@ -0,0 +1,29 @@
1
+ import { attrValue } from "../xml/query.js";
2
+ //#region src/xhtml/footnote.ts
3
+ const FOOTNOTE_CLASS_PATTERN = /footnote|noteref/i;
4
+ function epubTypeValues(element) {
5
+ const value = attrValue(element, "epub:type");
6
+ return value === void 0 ? [] : value.split(/\s+/u).filter((v) => v.length > 0);
7
+ }
8
+ function hasFootnoteClass(element) {
9
+ const className = attrValue(element, "class");
10
+ return className !== void 0 && FOOTNOTE_CLASS_PATTERN.test(className);
11
+ }
12
+ function sameDocumentFragment(href) {
13
+ if (href === void 0 || !href.startsWith("#") || href.length < 2) return;
14
+ return href.slice(1);
15
+ }
16
+ function isFootnoteReferenceAnchor(anchor, idElements) {
17
+ const fragment = sameDocumentFragment(attrValue(anchor, "href"));
18
+ if (fragment === void 0) return;
19
+ const target = idElements.get(fragment);
20
+ if (target === void 0) return;
21
+ if (epubTypeValues(anchor).some((v) => /noteref/i.test(v))) return fragment;
22
+ if (hasFootnoteClass(anchor) || hasFootnoteClass(target)) return fragment;
23
+ }
24
+ function isFootnoteAside(element) {
25
+ if (element.tag !== "aside") return false;
26
+ return epubTypeValues(element).some((v) => /footnote|rearnote/i.test(v));
27
+ }
28
+ //#endregion
29
+ export { isFootnoteAside, isFootnoteReferenceAnchor, sameDocumentFragment };
@@ -0,0 +1,128 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_diagnostics = require("../diagnostics.cjs");
3
+ const require_xml_query = require("../xml/query.cjs");
4
+ const require_xml_entities = require("../xml/entities.cjs");
5
+ const require_xhtml_footnote = require("./footnote.cjs");
6
+ const require_xhtml_style_constants = require("./style-constants.cjs");
7
+ //#region src/xhtml/inline.ts
8
+ function normalizeWhitespace(text) {
9
+ return text.replace(/[ \t\r\n]+/gu, " ");
10
+ }
11
+ function mergeStyle(outer, inner) {
12
+ return {
13
+ ...outer,
14
+ ...inner
15
+ };
16
+ }
17
+ function styledRun(text, style, hyperlink) {
18
+ const run = { text };
19
+ if (style.bold === true) run.bold = true;
20
+ if (style.italic === true) run.italic = true;
21
+ if (style.underline === true) run.underline = true;
22
+ if (style.strike === true) run.strike = true;
23
+ if (style.fontFamily !== void 0) run.fontFamily = style.fontFamily;
24
+ if (hyperlink !== void 0) run.hyperlink = hyperlink;
25
+ return run;
26
+ }
27
+ function buildInlineRuns(nodes, style, context) {
28
+ const runs = [];
29
+ const constructs = [];
30
+ for (const node of nodes) {
31
+ if (node.type === "text") {
32
+ const text = normalizeWhitespace(require_xml_entities.decodeEntities(node.value));
33
+ if (text.length > 0) runs.push(styledRun(text, style));
34
+ continue;
35
+ }
36
+ if (node.type !== "element") continue;
37
+ appendElement(node, style, context, runs, constructs);
38
+ }
39
+ return {
40
+ runs,
41
+ constructs
42
+ };
43
+ }
44
+ function appendElement(element, style, context, runs, constructs) {
45
+ switch (element.tag) {
46
+ case "strong":
47
+ case "b":
48
+ appendNested(element, mergeStyle(style, { bold: true }), context, runs, constructs);
49
+ return;
50
+ case "em":
51
+ case "i":
52
+ appendNested(element, mergeStyle(style, { italic: true }), context, runs, constructs);
53
+ return;
54
+ case "u":
55
+ appendNested(element, mergeStyle(style, { underline: true }), context, runs, constructs);
56
+ return;
57
+ case "s":
58
+ case "strike":
59
+ case "del":
60
+ appendNested(element, mergeStyle(style, { strike: true }), context, runs, constructs);
61
+ return;
62
+ case "code":
63
+ case "kbd":
64
+ case "samp":
65
+ appendNested(element, mergeStyle(style, { fontFamily: require_xhtml_style_constants.MONOSPACE_FONT_FAMILY }), context, runs, constructs);
66
+ return;
67
+ case "sub":
68
+ case "sup":
69
+ context.sink({
70
+ code: require_diagnostics.EpubDiagnosticCodes.ELEMENT_UNMAPPED,
71
+ severity: "info",
72
+ message: `<${element.tag}> has no document-schema.js run-level field to carry its vertical position; the text is kept, the styling is not`,
73
+ href: context.sourceHref
74
+ });
75
+ appendNested(element, style, context, runs, constructs);
76
+ return;
77
+ case "br":
78
+ runs.push(styledRun("\n", style));
79
+ return;
80
+ case "a":
81
+ appendAnchor(element, style, context, runs, constructs);
82
+ return;
83
+ default: appendNested(element, style, context, runs, constructs);
84
+ }
85
+ }
86
+ function appendNested(element, style, context, runs, constructs) {
87
+ const nested = buildInlineRuns(element.children, style, context);
88
+ runs.push(...nested.runs);
89
+ constructs.push(...nested.constructs);
90
+ }
91
+ function appendAnchor(element, style, context, runs, constructs) {
92
+ const footnoteName = require_xhtml_footnote.isFootnoteReferenceAnchor(element, context.idElements);
93
+ if (footnoteName !== void 0) {
94
+ const startRun = runs.length;
95
+ const nested = buildInlineRuns(element.children, style, context);
96
+ runs.push(...nested.runs);
97
+ constructs.push(...nested.constructs);
98
+ constructs.push({
99
+ descriptor: {
100
+ kind: "anchor",
101
+ anchorType: "footnote",
102
+ name: footnoteName
103
+ },
104
+ startRun,
105
+ endRun: runs.length
106
+ });
107
+ return;
108
+ }
109
+ const href = require_xml_query.attrValue(element, "href");
110
+ if (href === void 0 || href.length === 0) {
111
+ appendNested(element, style, context, runs, constructs);
112
+ return;
113
+ }
114
+ const nested = buildInlineRuns(element.children, style, context);
115
+ for (const run of nested.runs) runs.push({
116
+ ...run,
117
+ hyperlink: href
118
+ });
119
+ constructs.push(...nested.constructs);
120
+ if (require_xhtml_footnote.sameDocumentFragment(href) === void 0 && !/^[a-z][a-z0-9+.-]*:/iu.test(href)) context.sink({
121
+ code: require_diagnostics.EpubDiagnosticCodes.LINK_TARGET_EXTERNAL_ONLY,
122
+ severity: "info",
123
+ message: `href "${href}" carries no URI scheme and is not a same-document fragment; it is stored verbatim on ContentRun.hyperlink without resolution against the package's own manifest`,
124
+ href: context.sourceHref
125
+ });
126
+ }
127
+ //#endregion
128
+ exports.buildInlineRuns = buildInlineRuns;
@@ -0,0 +1,11 @@
1
+ import { XmlNode } from "../xml/node.cjs";
2
+ import { InlineStyle, XhtmlReadContext } from "./context.cjs";
3
+ import { ContentRun, RunConstructExtent } from "document-schema.js";
4
+ //#region src/xhtml/inline.d.ts
5
+ interface InlineResult {
6
+ readonly runs: ContentRun[];
7
+ readonly constructs: RunConstructExtent[];
8
+ }
9
+ declare function buildInlineRuns(nodes: readonly XmlNode[], style: InlineStyle, context: XhtmlReadContext): InlineResult;
10
+ //#endregion
11
+ export { InlineResult, buildInlineRuns };