@voidberg/quarto 0.1.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/README.md +157 -0
- package/assets/quarto.png +0 -0
- package/dist/css.d.ts +6 -0
- package/dist/css.js +109 -0
- package/dist/css.js.map +1 -0
- package/dist/epub.d.ts +8 -0
- package/dist/epub.js +232 -0
- package/dist/epub.js.map +1 -0
- package/dist/html.d.ts +54 -0
- package/dist/html.js +430 -0
- package/dist/html.js.map +1 -0
- package/dist/images.d.ts +24 -0
- package/dist/images.js +51 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/kepub.d.ts +10 -0
- package/dist/kepub.js +32 -0
- package/dist/kepub.js.map +1 -0
- package/dist/opf.d.ts +65 -0
- package/dist/opf.js +172 -0
- package/dist/opf.js.map +1 -0
- package/dist/types.d.ts +133 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +25 -0
- package/dist/util.js +157 -0
- package/dist/util.js.map +1 -0
- package/dist/zip.d.ts +9 -0
- package/dist/zip.js +22 -0
- package/dist/zip.js.map +1 -0
- package/package.json +61 -0
package/dist/html.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { parse, parseFragment } from "parse5";
|
|
2
|
+
const VOID_ELEMENTS = new Set([
|
|
3
|
+
"area",
|
|
4
|
+
"base",
|
|
5
|
+
"br",
|
|
6
|
+
"col",
|
|
7
|
+
"embed",
|
|
8
|
+
"hr",
|
|
9
|
+
"img",
|
|
10
|
+
"input",
|
|
11
|
+
"link",
|
|
12
|
+
"meta",
|
|
13
|
+
"param",
|
|
14
|
+
"source",
|
|
15
|
+
"track",
|
|
16
|
+
"wbr",
|
|
17
|
+
]);
|
|
18
|
+
// Inline elements whose content model is phrasing-only: they may not contain
|
|
19
|
+
// block/flow elements. (`a`, `ins`, `del`, `map` are transparent — excluded.)
|
|
20
|
+
const PHRASING_ONLY = new Set([
|
|
21
|
+
"span",
|
|
22
|
+
"em",
|
|
23
|
+
"strong",
|
|
24
|
+
"b",
|
|
25
|
+
"i",
|
|
26
|
+
"u",
|
|
27
|
+
"s",
|
|
28
|
+
"small",
|
|
29
|
+
"mark",
|
|
30
|
+
"code",
|
|
31
|
+
"kbd",
|
|
32
|
+
"samp",
|
|
33
|
+
"sub",
|
|
34
|
+
"sup",
|
|
35
|
+
"abbr",
|
|
36
|
+
"cite",
|
|
37
|
+
"q",
|
|
38
|
+
"dfn",
|
|
39
|
+
"time",
|
|
40
|
+
"var",
|
|
41
|
+
"bdi",
|
|
42
|
+
"bdo",
|
|
43
|
+
"big",
|
|
44
|
+
"tt",
|
|
45
|
+
]);
|
|
46
|
+
// Block/flow elements that may not appear inside phrasing content.
|
|
47
|
+
const BLOCK_ELEMENTS = new Set([
|
|
48
|
+
"div",
|
|
49
|
+
"p",
|
|
50
|
+
"section",
|
|
51
|
+
"article",
|
|
52
|
+
"aside",
|
|
53
|
+
"header",
|
|
54
|
+
"footer",
|
|
55
|
+
"nav",
|
|
56
|
+
"main",
|
|
57
|
+
"figure",
|
|
58
|
+
"figcaption",
|
|
59
|
+
"blockquote",
|
|
60
|
+
"ul",
|
|
61
|
+
"ol",
|
|
62
|
+
"li",
|
|
63
|
+
"dl",
|
|
64
|
+
"dt",
|
|
65
|
+
"dd",
|
|
66
|
+
"table",
|
|
67
|
+
"caption",
|
|
68
|
+
"thead",
|
|
69
|
+
"tbody",
|
|
70
|
+
"tfoot",
|
|
71
|
+
"tr",
|
|
72
|
+
"td",
|
|
73
|
+
"th",
|
|
74
|
+
"h1",
|
|
75
|
+
"h2",
|
|
76
|
+
"h3",
|
|
77
|
+
"h4",
|
|
78
|
+
"h5",
|
|
79
|
+
"h6",
|
|
80
|
+
"hr",
|
|
81
|
+
"pre",
|
|
82
|
+
"form",
|
|
83
|
+
"fieldset",
|
|
84
|
+
"address",
|
|
85
|
+
]);
|
|
86
|
+
function isText(node) {
|
|
87
|
+
return node.nodeName === "#text";
|
|
88
|
+
}
|
|
89
|
+
function isComment(node) {
|
|
90
|
+
return node.nodeName === "#comment";
|
|
91
|
+
}
|
|
92
|
+
function isElement(node) {
|
|
93
|
+
return "tagName" in node && Array.isArray(node.attrs);
|
|
94
|
+
}
|
|
95
|
+
function escapeText(value) {
|
|
96
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
97
|
+
}
|
|
98
|
+
function escapeAttr(value) {
|
|
99
|
+
// Only &, < and " need escaping in a double-quoted XML attribute value;
|
|
100
|
+
// regular spaces are valid and must be preserved (so e.g. multi-token class
|
|
101
|
+
// names and alt/title text survive intact).
|
|
102
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/"/g, """);
|
|
103
|
+
}
|
|
104
|
+
/** Serialize a parse5 node list as well-formed XHTML. */
|
|
105
|
+
function serializeNodes(nodes) {
|
|
106
|
+
let out = "";
|
|
107
|
+
for (const node of nodes) {
|
|
108
|
+
if (isText(node)) {
|
|
109
|
+
out += escapeText(node.value);
|
|
110
|
+
}
|
|
111
|
+
else if (isComment(node)) {
|
|
112
|
+
out += `<!--${node.data}-->`;
|
|
113
|
+
}
|
|
114
|
+
else if (isElement(node)) {
|
|
115
|
+
const tag = node.tagName;
|
|
116
|
+
const attrs = node.attrs
|
|
117
|
+
.map((a) => {
|
|
118
|
+
const name = a.prefix ? `${a.prefix}:${a.name}` : a.name;
|
|
119
|
+
return ` ${name}="${escapeAttr(a.value)}"`;
|
|
120
|
+
})
|
|
121
|
+
.join("");
|
|
122
|
+
if (VOID_ELEMENTS.has(tag) && node.childNodes.length === 0) {
|
|
123
|
+
out += `<${tag}${attrs}/>`;
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
out += `<${tag}${attrs}>${serializeNodes(node.childNodes)}</${tag}>`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/** Walk every element in a subtree, depth-first. */
|
|
133
|
+
function walkElements(nodes, visit) {
|
|
134
|
+
for (const node of nodes) {
|
|
135
|
+
if (isElement(node)) {
|
|
136
|
+
visit(node);
|
|
137
|
+
walkElements(node.childNodes, visit);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Browsers (and parse5) tolerate block elements nested inside phrasing-only
|
|
143
|
+
* inline elements like `<span><div>…</div></span>`, but that violates the XHTML
|
|
144
|
+
* content model and EPUBCheck rejects it. Bottom-up, rewrite any phrasing-only
|
|
145
|
+
* element that contains a block child into a `<div>` (keeping its attributes), so
|
|
146
|
+
* the structure becomes valid without losing content.
|
|
147
|
+
*/
|
|
148
|
+
function fixBlockInPhrasing(nodes) {
|
|
149
|
+
for (const node of nodes) {
|
|
150
|
+
if (!isElement(node))
|
|
151
|
+
continue;
|
|
152
|
+
fixBlockInPhrasing(node.childNodes);
|
|
153
|
+
if (PHRASING_ONLY.has(node.tagName) &&
|
|
154
|
+
node.childNodes.some((c) => isElement(c) && BLOCK_ELEMENTS.has(c.tagName))) {
|
|
155
|
+
node.nodeName = "div";
|
|
156
|
+
node.tagName = "div";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function getAttr(el, name) {
|
|
161
|
+
return el.attrs.find((a) => a.name === name)?.value;
|
|
162
|
+
}
|
|
163
|
+
function setAttr(el, name, value) {
|
|
164
|
+
const existing = el.attrs.find((a) => a.name === name);
|
|
165
|
+
if (existing)
|
|
166
|
+
existing.value = value;
|
|
167
|
+
else
|
|
168
|
+
el.attrs.push({ name, value });
|
|
169
|
+
}
|
|
170
|
+
/** A parsed HTML fragment ready to inspect, mutate and serialize. */
|
|
171
|
+
export class HtmlFragment {
|
|
172
|
+
nodes;
|
|
173
|
+
constructor(nodes) {
|
|
174
|
+
this.nodes = nodes;
|
|
175
|
+
}
|
|
176
|
+
static parse(html) {
|
|
177
|
+
const frag = parseFragment(html);
|
|
178
|
+
fixBlockInPhrasing(frag.childNodes);
|
|
179
|
+
return new HtmlFragment(frag.childNodes);
|
|
180
|
+
}
|
|
181
|
+
/** Collect the `src` of every `<img>` (in document order). */
|
|
182
|
+
imageSources() {
|
|
183
|
+
const srcs = [];
|
|
184
|
+
walkElements(this.nodes, (el) => {
|
|
185
|
+
if (el.tagName === "img") {
|
|
186
|
+
const src = getAttr(el, "src");
|
|
187
|
+
if (src)
|
|
188
|
+
srcs.push(src);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
return srcs;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Walk every `<img>` (depth-first, splice-safe) and apply a decision based on
|
|
195
|
+
* its `src`:
|
|
196
|
+
* - a string → rewrite `src` to it (and ensure `alt`);
|
|
197
|
+
* - `null` → remove the element;
|
|
198
|
+
* - `undefined` → leave it (and ensure `alt`).
|
|
199
|
+
*/
|
|
200
|
+
transformImages(decide) {
|
|
201
|
+
const process = (nodes) => {
|
|
202
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
203
|
+
const node = nodes[i];
|
|
204
|
+
if (!isElement(node))
|
|
205
|
+
continue;
|
|
206
|
+
if (node.tagName === "img") {
|
|
207
|
+
const result = decide(getAttr(node, "src"));
|
|
208
|
+
if (result === null) {
|
|
209
|
+
nodes.splice(i, 1);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
if (typeof result === "string")
|
|
213
|
+
setAttr(node, "src", result);
|
|
214
|
+
if (getAttr(node, "alt") === undefined)
|
|
215
|
+
setAttr(node, "alt", "");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
process(node.childNodes);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
process(this.nodes);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Resolve every `<img>` against an embedded-image map (original src → in-package
|
|
227
|
+
* path). Matched images are rewritten and given alt text. When
|
|
228
|
+
* `stripUnresolved` is set, images that were not embedded (failed downloads,
|
|
229
|
+
* unsupported sources) are removed entirely so the EPUB has no dangling
|
|
230
|
+
* foreign resources — EPUBCheck rejects those.
|
|
231
|
+
*/
|
|
232
|
+
resolveImages(map, stripUnresolved) {
|
|
233
|
+
this.transformImages((src) => {
|
|
234
|
+
if (src && map.has(src))
|
|
235
|
+
return map.get(src);
|
|
236
|
+
return stripUnresolved ? null : undefined;
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Remove every `<img>` that references something outside the container — i.e.
|
|
241
|
+
* anything that isn't an inline `data:` URI (remote URLs and relative paths
|
|
242
|
+
* alike). Used when image downloading is disabled: only self-contained `data:`
|
|
243
|
+
* images can survive, so this keeps the EPUB valid instead of leaving dangling
|
|
244
|
+
* references EPUBCheck rejects.
|
|
245
|
+
*/
|
|
246
|
+
stripExternalImages() {
|
|
247
|
+
this.transformImages((src) => (src && /^data:/i.test(src) ? undefined : null));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* If the fragment's first meaningful content is an image (i.e. no text
|
|
251
|
+
* precedes it), remove that image and return its `src` — for promoting an
|
|
252
|
+
* article's lead image to the cover. Returns `undefined` when text comes first,
|
|
253
|
+
* so we never pull an image out of the middle of the prose.
|
|
254
|
+
*/
|
|
255
|
+
extractLeadImage() {
|
|
256
|
+
let src;
|
|
257
|
+
// A wrapper is "empty" if nothing but whitespace/comments remains.
|
|
258
|
+
const isEmpty = (el) => el.childNodes.every((c) => isComment(c) || (isText(c) && c.value.trim() === ""));
|
|
259
|
+
const visit = (nodes) => {
|
|
260
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
261
|
+
const node = nodes[i];
|
|
262
|
+
if (isText(node)) {
|
|
263
|
+
if (node.value.trim() !== "")
|
|
264
|
+
return "text";
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (!isElement(node))
|
|
268
|
+
continue;
|
|
269
|
+
if (node.tagName === "img") {
|
|
270
|
+
src = getAttr(node, "src");
|
|
271
|
+
nodes.splice(i, 1);
|
|
272
|
+
return "img";
|
|
273
|
+
}
|
|
274
|
+
const inner = visit(node.childNodes);
|
|
275
|
+
if (inner === "img") {
|
|
276
|
+
// The lead image lived inside this wrapper; drop it too if now empty
|
|
277
|
+
// (a remaining <figcaption> etc. keeps it). Propagates up the nesting.
|
|
278
|
+
if (isEmpty(node))
|
|
279
|
+
nodes.splice(i, 1);
|
|
280
|
+
return "img";
|
|
281
|
+
}
|
|
282
|
+
if (inner === "text")
|
|
283
|
+
return "text";
|
|
284
|
+
}
|
|
285
|
+
return undefined;
|
|
286
|
+
};
|
|
287
|
+
return visit(this.nodes) === "img" ? src : undefined;
|
|
288
|
+
}
|
|
289
|
+
serialize() {
|
|
290
|
+
return serializeNodes(this.nodes);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// --- kepub transform ---------------------------------------------------------
|
|
294
|
+
// Block-level tags whose text Kobo expects to be segmented for location tracking.
|
|
295
|
+
// (`pre` is intentionally absent — it lives in KOBO_SKIP, whose check runs first.)
|
|
296
|
+
const KOBO_SEGMENT_BLOCKS = new Set([
|
|
297
|
+
"p",
|
|
298
|
+
"li",
|
|
299
|
+
"h1",
|
|
300
|
+
"h2",
|
|
301
|
+
"h3",
|
|
302
|
+
"h4",
|
|
303
|
+
"h5",
|
|
304
|
+
"h6",
|
|
305
|
+
"blockquote",
|
|
306
|
+
"td",
|
|
307
|
+
"th",
|
|
308
|
+
"figcaption",
|
|
309
|
+
"dd",
|
|
310
|
+
"dt",
|
|
311
|
+
]);
|
|
312
|
+
// Never wrap text inside these.
|
|
313
|
+
const KOBO_SKIP = new Set(["script", "style", "pre", "audio", "video", "svg", "math"]);
|
|
314
|
+
/**
|
|
315
|
+
* Transform an XHTML content document into its Kobo (kepub) form, mirroring
|
|
316
|
+
* kepubify:
|
|
317
|
+
*
|
|
318
|
+
* - every text run inside a block element is wrapped in
|
|
319
|
+
* `<span class="koboSpan" id="kobo.{segment}.{fragment}">` so the firmware can
|
|
320
|
+
* track reading position and anchor highlights precisely;
|
|
321
|
+
* - the body content is wrapped in `<div id="book-columns"><div id="book-inner">`
|
|
322
|
+
* which Kobo relies on for pagination and justification.
|
|
323
|
+
*
|
|
324
|
+
* Returns a complete XHTML document string. If the input has no parseable
|
|
325
|
+
* `<body>`, it is returned unchanged.
|
|
326
|
+
*/
|
|
327
|
+
export function kepubifyXhtml(documentHtml) {
|
|
328
|
+
const doc = parse(documentHtml);
|
|
329
|
+
const html = findElement(doc.childNodes, "html");
|
|
330
|
+
const body = html ? findElement(html.childNodes, "body") : undefined;
|
|
331
|
+
if (!html || !body)
|
|
332
|
+
return documentHtml;
|
|
333
|
+
const counter = { segment: 0 };
|
|
334
|
+
wrapTextNodes(body.childNodes, counter);
|
|
335
|
+
const inner = {
|
|
336
|
+
nodeName: "div",
|
|
337
|
+
tagName: "div",
|
|
338
|
+
attrs: [{ name: "id", value: "book-inner" }],
|
|
339
|
+
childNodes: body.childNodes,
|
|
340
|
+
};
|
|
341
|
+
const columns = {
|
|
342
|
+
nodeName: "div",
|
|
343
|
+
tagName: "div",
|
|
344
|
+
attrs: [{ name: "id", value: "book-columns" }],
|
|
345
|
+
childNodes: [inner],
|
|
346
|
+
};
|
|
347
|
+
body.childNodes = [columns];
|
|
348
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE html>\n${serializeNodes([html])}\n`;
|
|
349
|
+
}
|
|
350
|
+
function findElement(nodes, tagName) {
|
|
351
|
+
for (const node of nodes) {
|
|
352
|
+
if (isElement(node)) {
|
|
353
|
+
if (node.tagName === tagName)
|
|
354
|
+
return node;
|
|
355
|
+
const nested = findElement(node.childNodes, tagName);
|
|
356
|
+
if (nested)
|
|
357
|
+
return nested;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return undefined;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Recurse into the tree, treating each block in {@link KOBO_SEGMENT_BLOCKS} as
|
|
364
|
+
* one location segment and wrapping its text via {@link wrapInline}.
|
|
365
|
+
*
|
|
366
|
+
* Known simplification vs. full kepubify: only text inside a recognised block
|
|
367
|
+
* is wrapped. Loose text that sits directly in a non-block container (e.g. a
|
|
368
|
+
* bare text node under a `<div>`) is left unwrapped, and a block nested inside
|
|
369
|
+
* another block shares its parent's segment number. Content that quarto itself
|
|
370
|
+
* generates is always block-wrapped, so this only affects arbitrary inputs and
|
|
371
|
+
* costs Kobo a little location-tracking granularity — never validity.
|
|
372
|
+
*/
|
|
373
|
+
function wrapTextNodes(nodes, counter) {
|
|
374
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
375
|
+
const node = nodes[i];
|
|
376
|
+
if (isElement(node)) {
|
|
377
|
+
if (KOBO_SKIP.has(node.tagName))
|
|
378
|
+
continue;
|
|
379
|
+
if (KOBO_SEGMENT_BLOCKS.has(node.tagName)) {
|
|
380
|
+
counter.segment++;
|
|
381
|
+
wrapInline(node, counter.segment);
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
wrapTextNodes(node.childNodes, counter);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Wrap each non-empty text node within a block in its own koboSpan, numbering
|
|
391
|
+
* fragments sequentially. Nested inline elements keep their structure; only the
|
|
392
|
+
* text leaves are wrapped.
|
|
393
|
+
*/
|
|
394
|
+
function wrapInline(block, segment) {
|
|
395
|
+
const fragment = { n: 0 };
|
|
396
|
+
const transform = (nodes) => {
|
|
397
|
+
const result = [];
|
|
398
|
+
for (const node of nodes) {
|
|
399
|
+
if (isText(node)) {
|
|
400
|
+
if (node.value.trim() === "") {
|
|
401
|
+
result.push(node);
|
|
402
|
+
continue;
|
|
403
|
+
}
|
|
404
|
+
fragment.n++;
|
|
405
|
+
result.push(koboSpan(segment, fragment.n, [node]));
|
|
406
|
+
}
|
|
407
|
+
else if (isElement(node) && !KOBO_SKIP.has(node.tagName)) {
|
|
408
|
+
node.childNodes = transform(node.childNodes);
|
|
409
|
+
result.push(node);
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
result.push(node);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return result;
|
|
416
|
+
};
|
|
417
|
+
block.childNodes = transform(block.childNodes);
|
|
418
|
+
}
|
|
419
|
+
function koboSpan(segment, fragment, children) {
|
|
420
|
+
return {
|
|
421
|
+
nodeName: "span",
|
|
422
|
+
tagName: "span",
|
|
423
|
+
attrs: [
|
|
424
|
+
{ name: "class", value: "koboSpan" },
|
|
425
|
+
{ name: "id", value: `kobo.${segment}.${fragment}` },
|
|
426
|
+
],
|
|
427
|
+
childNodes: children,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
//# sourceMappingURL=html.js.map
|
package/dist/html.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html.js","sourceRoot":"","sources":["../src/html.ts"],"names":[],"mappings":"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"}
|
package/dist/images.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ImageTransform } from "./types.js";
|
|
2
|
+
export interface EmbeddedImage {
|
|
3
|
+
/** Path inside the EPUB, e.g. `images/img-1.jpg`. */
|
|
4
|
+
path: string;
|
|
5
|
+
/** Manifest id. */
|
|
6
|
+
id: string;
|
|
7
|
+
mime: string;
|
|
8
|
+
data: Uint8Array;
|
|
9
|
+
}
|
|
10
|
+
/** Fetch a single image URL (or accept raw bytes) and classify its type. */
|
|
11
|
+
export declare function fetchImage(source: string | Uint8Array, fetcher: typeof fetch): Promise<{
|
|
12
|
+
data: Uint8Array;
|
|
13
|
+
mime: string;
|
|
14
|
+
}>;
|
|
15
|
+
/**
|
|
16
|
+
* Download a set of image sources concurrently, skipping any that fail, return
|
|
17
|
+
* no bytes, or aren't an EPUB core image type (the book stays valid even when a
|
|
18
|
+
* source 404s or is unsupported). Returns the embedded images plus a map from
|
|
19
|
+
* original URL → in-package path for rewriting.
|
|
20
|
+
*/
|
|
21
|
+
export declare function embedImages(urls: string[], fetcher: typeof fetch, transform?: ImageTransform): Promise<{
|
|
22
|
+
images: EmbeddedImage[];
|
|
23
|
+
rewrites: Map<string, string>;
|
|
24
|
+
}>;
|
package/dist/images.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { extFromMime, isCoreImageType, mimeFromUrl, sniffImageMime } from "./util.js";
|
|
2
|
+
/** Fetch a single image URL (or accept raw bytes) and classify its type. */
|
|
3
|
+
export async function fetchImage(source, fetcher) {
|
|
4
|
+
if (typeof source !== "string") {
|
|
5
|
+
return { data: source, mime: sniffImageMime(source) ?? "image/jpeg" };
|
|
6
|
+
}
|
|
7
|
+
const res = await fetcher(source);
|
|
8
|
+
if (!res.ok)
|
|
9
|
+
throw new Error(`Failed to fetch image ${source}: HTTP ${res.status}`);
|
|
10
|
+
const data = new Uint8Array(await res.arrayBuffer());
|
|
11
|
+
const headerMime = res.headers.get("content-type")?.split(";")[0]?.trim();
|
|
12
|
+
const mime = sniffImageMime(data) ||
|
|
13
|
+
(headerMime?.startsWith("image/") ? headerMime : undefined) ||
|
|
14
|
+
mimeFromUrl(source) ||
|
|
15
|
+
"image/jpeg";
|
|
16
|
+
return { data, mime };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Download a set of image sources concurrently, skipping any that fail, return
|
|
20
|
+
* no bytes, or aren't an EPUB core image type (the book stays valid even when a
|
|
21
|
+
* source 404s or is unsupported). Returns the embedded images plus a map from
|
|
22
|
+
* original URL → in-package path for rewriting.
|
|
23
|
+
*/
|
|
24
|
+
export async function embedImages(urls, fetcher, transform) {
|
|
25
|
+
const unique = [...new Set(urls)].filter((u) => u.length > 0);
|
|
26
|
+
const images = [];
|
|
27
|
+
const rewrites = new Map();
|
|
28
|
+
const results = await Promise.allSettled(unique.map(async (url) => {
|
|
29
|
+
const img = await fetchImage(url, fetcher);
|
|
30
|
+
return transform ? await transform(img) : img;
|
|
31
|
+
}));
|
|
32
|
+
let counter = 0;
|
|
33
|
+
results.forEach((result, i) => {
|
|
34
|
+
const url = unique[i];
|
|
35
|
+
// Skip anything we can't embed: failed fetches, transform-dropped (null) or
|
|
36
|
+
// empty payloads, and types EPUB can't carry without a fallback — the
|
|
37
|
+
// corresponding <img> is then stripped so the EPUB stays valid.
|
|
38
|
+
if (result.status !== "fulfilled" || result.value === null)
|
|
39
|
+
return;
|
|
40
|
+
if (result.value.data.length === 0 || !isCoreImageType(result.value.mime))
|
|
41
|
+
return;
|
|
42
|
+
counter++;
|
|
43
|
+
const ext = extFromMime(result.value.mime);
|
|
44
|
+
const path = `images/img-${counter}.${ext}`;
|
|
45
|
+
const id = `img-${counter}`;
|
|
46
|
+
images.push({ path, id, mime: result.value.mime, data: result.value.data });
|
|
47
|
+
rewrites.set(url, path);
|
|
48
|
+
});
|
|
49
|
+
return { images, rewrites };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"images.js","sourceRoot":"","sources":["../src/images.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { DEFAULT_CSS } from "./css.js";
|
|
2
|
+
export { generateEpub } from "./epub.js";
|
|
3
|
+
export { toKepub } from "./kepub.js";
|
|
4
|
+
export type { Chapter, CoverTransform, EpubInput, EpubOptions, ImageSource, ImageTransform, RawImage, } from "./types.js";
|
|
5
|
+
export { imageSize } from "./util.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAUrC,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/kepub.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a standard EPUB into a Kobo kepub in memory — the equivalent of
|
|
3
|
+
* running `kepubify`, with no external binary required.
|
|
4
|
+
*
|
|
5
|
+
* Every content document is rewritten with Kobo reading-location spans and the
|
|
6
|
+
* `book-columns`/`book-inner` wrappers. The navigation document is left
|
|
7
|
+
* untouched. The returned bytes should be written with a `.kepub.epub`
|
|
8
|
+
* extension so Kobo devices recognise the enhanced format.
|
|
9
|
+
*/
|
|
10
|
+
export declare function toKepub(epub: Uint8Array): Uint8Array;
|
package/dist/kepub.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { kepubifyXhtml } from "./html.js";
|
|
2
|
+
import { decodeUtf8, encodeUtf8 } from "./util.js";
|
|
3
|
+
import { unzipEpub, zipEpub } from "./zip.js";
|
|
4
|
+
const XHTML_RE = /\.x?html?$/i;
|
|
5
|
+
const NAV_RE = /epub:type=["']toc["']/;
|
|
6
|
+
/**
|
|
7
|
+
* Convert a standard EPUB into a Kobo kepub in memory — the equivalent of
|
|
8
|
+
* running `kepubify`, with no external binary required.
|
|
9
|
+
*
|
|
10
|
+
* Every content document is rewritten with Kobo reading-location spans and the
|
|
11
|
+
* `book-columns`/`book-inner` wrappers. The navigation document is left
|
|
12
|
+
* untouched. The returned bytes should be written with a `.kepub.epub`
|
|
13
|
+
* extension so Kobo devices recognise the enhanced format.
|
|
14
|
+
*/
|
|
15
|
+
export function toKepub(epub) {
|
|
16
|
+
const entries = unzipEpub(epub);
|
|
17
|
+
const files = {};
|
|
18
|
+
for (const [path, bytes] of Object.entries(entries)) {
|
|
19
|
+
if (path === "mimetype")
|
|
20
|
+
continue; // re-added (stored, first) by zipEpub
|
|
21
|
+
if (XHTML_RE.test(path)) {
|
|
22
|
+
const html = decodeUtf8(bytes);
|
|
23
|
+
// Skip the navigation document; Kobo builds its TOC from it directly.
|
|
24
|
+
files[path] = NAV_RE.test(html) ? bytes : encodeUtf8(kepubifyXhtml(html));
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
files[path] = bytes;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return zipEpub(files);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=kepub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kepub.js","sourceRoot":"","sources":["../src/kepub.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAE9C,MAAM,QAAQ,GAAG,aAAa,CAAC;AAC/B,MAAM,MAAM,GAAG,uBAAuB,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,UAAU,OAAO,CAAC,IAAgB;IACtC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,KAAK,GAA+B,EAAE,CAAC;IAE7C,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,IAAI,IAAI,KAAK,UAAU;YAAE,SAAS,CAAC,sCAAsC;QACzE,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;YAC/B,sEAAsE;YACtE,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5E,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
|
package/dist/opf.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface ManifestItem {
|
|
2
|
+
id: string;
|
|
3
|
+
href: string;
|
|
4
|
+
mediaType: string;
|
|
5
|
+
/** EPUB3 manifest properties, e.g. `"nav"` or `"cover-image"`. */
|
|
6
|
+
properties?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface NavEntry {
|
|
9
|
+
href: string;
|
|
10
|
+
title: string;
|
|
11
|
+
}
|
|
12
|
+
/** A reference for the EPUB2 `<guide>` or the EPUB3 landmarks nav (e.g. cover, start). */
|
|
13
|
+
export interface Reference {
|
|
14
|
+
type: string;
|
|
15
|
+
href: string;
|
|
16
|
+
title: string;
|
|
17
|
+
}
|
|
18
|
+
export interface OpfMetadata {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
authors: string[];
|
|
22
|
+
language: string;
|
|
23
|
+
publisher?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
date?: string;
|
|
26
|
+
coverImageId?: string;
|
|
27
|
+
}
|
|
28
|
+
export declare const CONTAINER_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">\n <rootfiles>\n <rootfile full-path=\"EPUB/content.opf\" media-type=\"application/oebps-package+xml\"/>\n </rootfiles>\n</container>\n";
|
|
29
|
+
export declare function buildOpf(meta: OpfMetadata, manifest: ManifestItem[], spine: string[], options?: {
|
|
30
|
+
ncxId?: string;
|
|
31
|
+
guide?: Reference[];
|
|
32
|
+
}): string;
|
|
33
|
+
/**
|
|
34
|
+
* EPUB3 navigation document. EPUB3 mandates exactly one `nav` document, so even
|
|
35
|
+
* when a book opts out of a table of contents we still emit one — but mark it
|
|
36
|
+
* `hidden` and keep it out of the spine so no visible TOC page appears.
|
|
37
|
+
*/
|
|
38
|
+
export declare function buildNav(navTitle: string, language: string, entries: NavEntry[], hidden?: boolean, landmarks?: Reference[]): string;
|
|
39
|
+
/** EPUB2 NCX, kept for older e-reader compatibility. */
|
|
40
|
+
export declare function buildNcx(id: string, title: string, entries: NavEntry[]): string;
|
|
41
|
+
export interface SvgCoverOptions {
|
|
42
|
+
title: string;
|
|
43
|
+
language: string;
|
|
44
|
+
imageHref: string;
|
|
45
|
+
width: number;
|
|
46
|
+
height: number;
|
|
47
|
+
/** Fills the letterbox bands around the cover image (e.g. to match its edges). */
|
|
48
|
+
background?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A cover page that wraps the image in an SVG scaled to fit the viewport
|
|
52
|
+
* (`preserveAspectRatio="xMidYMid meet"`). This is the portable way to make a
|
|
53
|
+
* cover fill and center consistently across readers — inline `<img>` layout
|
|
54
|
+
* anchors awkwardly on e-ink devices like Kobo.
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildSvgCover(opts: SvgCoverOptions): string;
|
|
57
|
+
export interface XhtmlPageOptions {
|
|
58
|
+
title: string;
|
|
59
|
+
language: string;
|
|
60
|
+
cssHref?: string;
|
|
61
|
+
bodyHtml: string;
|
|
62
|
+
bodyClass?: string;
|
|
63
|
+
}
|
|
64
|
+
/** Wrap body HTML in a complete XHTML content document. */
|
|
65
|
+
export declare function buildXhtml(opts: XhtmlPageOptions): string;
|