frond-js 0.4.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +247 -0
  3. package/README.md +211 -0
  4. package/dist/abort-BY8vBk0v.d.cts +99 -0
  5. package/dist/abort-BY8vBk0v.d.ts +99 -0
  6. package/dist/adapter-3G46J3CA.cjs +503 -0
  7. package/dist/adapter-3ONQWJVQ.js +501 -0
  8. package/dist/adapter-55QHWRSE.js +124 -0
  9. package/dist/adapter-DF34GBWJ.cjs +19 -0
  10. package/dist/adapter-EXTNILTC.cjs +126 -0
  11. package/dist/adapter-GOFC7TMC.js +284 -0
  12. package/dist/adapter-LRJTQ47I.cjs +286 -0
  13. package/dist/adapter-TWWZML4A.js +17 -0
  14. package/dist/adapter-ZM5FQTJT.js +1415 -0
  15. package/dist/adapter-ZRNSDQUV.cjs +1417 -0
  16. package/dist/chunk-2SUG7YFZ.cjs +108 -0
  17. package/dist/chunk-B2L2YVXD.js +89 -0
  18. package/dist/chunk-D4KWSEZD.js +393 -0
  19. package/dist/chunk-EZTIZO6R.cjs +430 -0
  20. package/dist/chunk-G7DLWGBW.cjs +103 -0
  21. package/dist/chunk-GTGLDLJD.cjs +479 -0
  22. package/dist/chunk-IIV6VIUJ.cjs +83 -0
  23. package/dist/chunk-JDTHZQUK.js +102 -0
  24. package/dist/chunk-JJVXT3AC.js +30 -0
  25. package/dist/chunk-LIXRYQL2.js +473 -0
  26. package/dist/chunk-NABYHI6X.cjs +400 -0
  27. package/dist/chunk-SJVOYNTF.js +425 -0
  28. package/dist/chunk-SLI2YL25.cjs +252 -0
  29. package/dist/chunk-U4264IQH.js +78 -0
  30. package/dist/chunk-UY2YRCFC.js +250 -0
  31. package/dist/chunk-WCZTTQ7Z.cjs +32 -0
  32. package/dist/core/index.cjs +162 -0
  33. package/dist/core/index.d.cts +321 -0
  34. package/dist/core/index.d.ts +321 -0
  35. package/dist/core/index.js +49 -0
  36. package/dist/default-DRLIJX73.js +1183 -0
  37. package/dist/default-UK52WOO5.cjs +1192 -0
  38. package/dist/formats/epub/index.cjs +29 -0
  39. package/dist/formats/epub/index.d.cts +286 -0
  40. package/dist/formats/epub/index.d.ts +286 -0
  41. package/dist/formats/epub/index.js +11 -0
  42. package/dist/index.cjs +655 -0
  43. package/dist/index.d.cts +335 -0
  44. package/dist/index.d.ts +335 -0
  45. package/dist/index.js +600 -0
  46. package/dist/render/index.cjs +2 -0
  47. package/dist/render/index.d.cts +116 -0
  48. package/dist/render/index.d.ts +116 -0
  49. package/dist/render/index.js +1 -0
  50. package/dist/types-B76GOMxj.d.ts +129 -0
  51. package/dist/types-B7mslPBY.d.cts +166 -0
  52. package/dist/types-B7mslPBY.d.ts +166 -0
  53. package/dist/types-BH88rUYt.d.cts +129 -0
  54. package/dist/types-C-5eHSRH.d.ts +379 -0
  55. package/dist/types-CPUqTEPW.d.cts +379 -0
  56. package/dist/types-DQYmArgv.d.cts +17 -0
  57. package/dist/types-DQYmArgv.d.ts +17 -0
  58. package/package.json +115 -0
@@ -0,0 +1,126 @@
1
+ 'use strict';
2
+
3
+ var chunk2SUG7YFZ_cjs = require('./chunk-2SUG7YFZ.cjs');
4
+ var chunkSLI2YL25_cjs = require('./chunk-SLI2YL25.cjs');
5
+ require('./chunk-IIV6VIUJ.cjs');
6
+ var chunkG7DLWGBW_cjs = require('./chunk-G7DLWGBW.cjs');
7
+
8
+ // src/formats/cbz/pages.ts
9
+ var CBZ_IMAGE_MEDIA_TYPES = {
10
+ ".jpg": "image/jpeg",
11
+ ".jpeg": "image/jpeg",
12
+ ".png": "image/png",
13
+ ".gif": "image/gif",
14
+ ".webp": "image/webp",
15
+ ".avif": "image/avif"
16
+ };
17
+ function collectCbzPages(names) {
18
+ const pages = [];
19
+ for (const name of names) {
20
+ const extension = imageExtensionOf(name);
21
+ if (extension === void 0) continue;
22
+ pages.push({
23
+ name,
24
+ mediaType: CBZ_IMAGE_MEDIA_TYPES[extension],
25
+ // ⚠️ 页序**只有这一个来源**:`pages` 数组的下标。
26
+ // 不排序、不按归档位置编号(跳过的非图片条目**不留空号**)。
27
+ index: pages.length
28
+ });
29
+ }
30
+ return pages;
31
+ }
32
+ function imageExtensionOf(name) {
33
+ const lowered = name.toLowerCase();
34
+ return chunk2SUG7YFZ_cjs.IMAGE_EXTENSIONS.find((extension) => lowered.endsWith(extension));
35
+ }
36
+ function archiveRootReference(name) {
37
+ return `/${name}`;
38
+ }
39
+
40
+ // src/formats/cbz/chapters.ts
41
+ var CBZ_CHAPTER_MEDIA_TYPE = "application/xhtml+xml";
42
+ var CBZ_PAGE_STYLE = "html,body{height:100%;margin:0}img{display:block;max-width:100%;max-height:100%;margin:auto}";
43
+ var DOCUMENT_HEAD = `<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml"><head><style>${CBZ_PAGE_STYLE}</style></head><body>`;
44
+ var DOCUMENT_TAIL = "</body></html>";
45
+ var NAME_WIDTH = 4;
46
+ function buildCbzChapters(pages) {
47
+ return pages.map((page, position) => {
48
+ const href = chapterHref(position);
49
+ return {
50
+ href,
51
+ item: { href, mediaType: CBZ_CHAPTER_MEDIA_TYPE, linear: true },
52
+ document: `${DOCUMENT_HEAD}<img src="${escapeAttribute(archiveRootReference(page.name))}"/>${DOCUMENT_TAIL}`
53
+ };
54
+ });
55
+ }
56
+ function chapterHref(position) {
57
+ return `text/${String(position).padStart(NAME_WIDTH, "0")}.xhtml`;
58
+ }
59
+ function escapeAttribute(value) {
60
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
61
+ }
62
+
63
+ // src/formats/cbz/label.ts
64
+ function sourceSuffix(label) {
65
+ return label === void 0 ? "" : `\uFF08\u6E90 ${label}\uFF09`;
66
+ }
67
+
68
+ // src/formats/cbz/open.ts
69
+ function openCbz(bytes, options) {
70
+ const source = options?.source;
71
+ return Promise.resolve().then(() => {
72
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
73
+ const archive = chunkSLI2YL25_cjs.parseZip(bytes, source === void 0 ? void 0 : { source });
74
+ const pages = collectCbzPages(archive.entries.map((entry) => entry.name));
75
+ if (pages.length === 0) {
76
+ throw new chunkG7DLWGBW_cjs.FormatError(`\u5F52\u6863\u91CC\u4E00\u4E2A\u56FE\u7247\u6761\u76EE\u90FD\u6CA1\u6709${sourceSuffix(source)}`);
77
+ }
78
+ const chapters = buildCbzChapters(pages);
79
+ return {
80
+ content: createCbzContent(archive, chapters),
81
+ book: buildBook(pages, chapters),
82
+ toc: []
83
+ };
84
+ });
85
+ }
86
+ function createCbzContent(archive, chapters) {
87
+ const documents = new Map(chapters.map((chapter) => [chapter.href, chapter.document]));
88
+ const read = (name, readOptions) => {
89
+ const document = documents.get(name);
90
+ if (document === void 0) return archive.read(name, readOptions);
91
+ return Promise.resolve().then(() => new TextEncoder().encode(document));
92
+ };
93
+ const readText = async (name, readOptions) => {
94
+ const document = documents.get(name);
95
+ return document === void 0 ? archive.readText(name, readOptions) : document;
96
+ };
97
+ return { read, readText };
98
+ }
99
+ function buildBook(pages, chapters) {
100
+ const resources = pages.map((page) => ({
101
+ href: page.name,
102
+ mediaType: page.mediaType
103
+ }));
104
+ return {
105
+ format: "cbz",
106
+ metadata: emptyMetadata(),
107
+ resources,
108
+ chapters: chapters.map((chapter) => chapter.item),
109
+ manifest: [],
110
+ spine: [],
111
+ manifestIndex: /* @__PURE__ */ new Map()
112
+ };
113
+ }
114
+ function emptyMetadata() {
115
+ return { title: "", creators: [], language: "", identifier: "" };
116
+ }
117
+
118
+ // src/formats/cbz/adapter.ts
119
+ var cbzAdapter = {
120
+ format: "cbz",
121
+ async open(bytes, options) {
122
+ return openCbz(bytes, options);
123
+ }
124
+ };
125
+
126
+ exports.cbzAdapter = cbzAdapter;
@@ -0,0 +1,284 @@
1
+ import { throwIfAborted, FormatError } from './chunk-B2L2YVXD.js';
2
+
3
+ // src/formats/pdf/chapters.ts
4
+ var PDF_CHAPTER_MEDIA_TYPE = "application/xhtml+xml";
5
+ var PDF_PAGE_STYLE = "html,body{height:100%;margin:0}img{display:block;max-width:100%;max-height:100%;margin:auto}";
6
+ var NAME_WIDTH = 4;
7
+ function pdfPageImagePath(index) {
8
+ return `page/${String(index).padStart(NAME_WIDTH, "0")}.png`;
9
+ }
10
+ function pdfPageImageReference(index) {
11
+ return `/${pdfPageImagePath(index)}`;
12
+ }
13
+ var PAGE_IMAGE_NAME = /^page\/(\d+)\.png$/;
14
+ function pdfPageImageIndex(name) {
15
+ const digits = PAGE_IMAGE_NAME.exec(name)?.[1];
16
+ if (digits === void 0) return void 0;
17
+ const index = Number(digits);
18
+ return pdfPageImagePath(index) === name ? index : void 0;
19
+ }
20
+ function pdfPageHref(index) {
21
+ return `page/${String(index).padStart(NAME_WIDTH, "0")}.xhtml`;
22
+ }
23
+ function buildPdfChapters(numPages, options) {
24
+ const images = options?.images === true;
25
+ return Array.from({ length: numPages }, (_unused, index) => ({
26
+ item: {
27
+ href: pdfPageHref(index),
28
+ mediaType: PDF_CHAPTER_MEDIA_TYPE,
29
+ linear: true
30
+ },
31
+ document: images ? pageImageDocument(index) : pageDocument(index, numPages)
32
+ }));
33
+ }
34
+ function pageDocument(index, numPages) {
35
+ const page = String(index + 1);
36
+ const total = String(numPages);
37
+ return `<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml"><head><title>\u7B2C ${page} \u9875</title></head><body><p>\u7B2C ${page} \u9875 / \u5171 ${total} \u9875</p><p>\u672C\u9875\u662F PDF \u7684\u56FA\u5B9A\u7248\u5F0F\u5185\u5BB9\uFF0C\u9700\u8981\u5BBF\u4E3B\u6E32\u67D3\u540E\u663E\u793A\u3002</p></body></html>`;
38
+ }
39
+ function pageImageDocument(index) {
40
+ const page = String(index + 1);
41
+ return `<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml"><head><title>\u7B2C ${page} \u9875</title><style>${PDF_PAGE_STYLE}</style></head><body><img src="${pdfPageImageReference(index)}"/></body></html>`;
42
+ }
43
+
44
+ // src/formats/pdf/label.ts
45
+ function sourceSuffix(label) {
46
+ return label === void 0 ? "" : `\uFF08\u6E90 ${label}\uFF09`;
47
+ }
48
+
49
+ // src/formats/pdf/loader.ts
50
+ var defaultPdfJsLoader = () => import('pdfjs-dist/legacy/build/pdf.mjs');
51
+ async function loadPdfJs(loader) {
52
+ try {
53
+ return await loader();
54
+ } catch (error) {
55
+ throw new FormatError(
56
+ `PDF \u652F\u6301\u9700\u8981\u53EF\u9009\u4F9D\u8D56 pdfjs-dist\uFF0C\u4F46\u5B83\u672A\u5B89\u88C5\u6216\u52A0\u8F7D\u5931\u8D25\u3002\u8BF7\u5148\u5B89\u88C5\uFF1Anpm i pdfjs-dist`,
57
+ { cause: error }
58
+ );
59
+ }
60
+ }
61
+
62
+ // src/formats/pdf/outline.ts
63
+ async function mapPdfOutline(doc, hrefForPage, options) {
64
+ throwIfAborted(options?.signal);
65
+ const outline = await doc.getOutline();
66
+ if (outline === null) return [];
67
+ return mapNodes(outline);
68
+ async function mapNodes(nodes) {
69
+ const items = [];
70
+ for (const node of nodes) {
71
+ throwIfAborted(options?.signal);
72
+ const index = await pageIndexOf(doc, node.dest);
73
+ const children = await mapNodes(node.items);
74
+ if (index === void 0) {
75
+ items.push(...children);
76
+ continue;
77
+ }
78
+ items.push({ label: node.title, href: hrefForPage(index), children });
79
+ }
80
+ return items;
81
+ }
82
+ }
83
+ async function pageIndexOf(doc, dest) {
84
+ try {
85
+ if (typeof dest === "string") {
86
+ const explicit = await doc.getDestination(dest);
87
+ if (explicit === null) return void 0;
88
+ return await doc.getPageIndex(explicit[0]);
89
+ }
90
+ if (dest === null) return void 0;
91
+ return await doc.getPageIndex(dest[0]);
92
+ } catch {
93
+ return void 0;
94
+ }
95
+ }
96
+
97
+ // src/formats/pdf/raster.ts
98
+ var PDF_RASTER_SCALE = 2;
99
+ var PDF_RASTER_MIME = "image/png";
100
+ var PDF_RASTER_CACHE_BYTES = 16 * 1024 * 1024;
101
+ function canRasterize() {
102
+ return typeof globalThis.OffscreenCanvas === "function";
103
+ }
104
+ var defaultRasterCanvasFactory = (width, height) => {
105
+ const ctor = globalThis.OffscreenCanvas;
106
+ if (ctor === void 0) {
107
+ throw new FormatError("\u672C\u73AF\u5883\u6CA1\u6709 OffscreenCanvas\uFF0C\u65E0\u6CD5\u628A PDF \u9875\u6E32\u67D3\u6210\u56FE\u7247");
108
+ }
109
+ return new ctor(width, height);
110
+ };
111
+ function createPdfRasterizer(doc, factory, options) {
112
+ const budget = PDF_RASTER_CACHE_BYTES;
113
+ const cache = /* @__PURE__ */ new Map();
114
+ let cachedBytes = 0;
115
+ const inFlight = /* @__PURE__ */ new Map();
116
+ const remember = (pageIndex, bytes) => {
117
+ const size = bytes.byteLength;
118
+ while (cachedBytes + size > budget && cache.size > 0) {
119
+ for (const [oldestPage, oldestBytes] of cache) {
120
+ cache.delete(oldestPage);
121
+ cachedBytes -= oldestBytes.byteLength;
122
+ break;
123
+ }
124
+ }
125
+ if (cachedBytes + size > budget) return;
126
+ cache.set(pageIndex, bytes);
127
+ cachedBytes += size;
128
+ };
129
+ const touch = (pageIndex, bytes) => {
130
+ cache.delete(pageIndex);
131
+ cache.set(pageIndex, bytes);
132
+ };
133
+ const renderPage = async (pageIndex) => {
134
+ try {
135
+ const page = await doc.getPage(pageIndex + 1);
136
+ const viewport = page.getViewport({ scale: PDF_RASTER_SCALE });
137
+ const canvas = factory(Math.ceil(viewport.width), Math.ceil(viewport.height));
138
+ await page.render({ canvas, viewport }).promise;
139
+ const blob = await canvas.convertToBlob({ type: PDF_RASTER_MIME });
140
+ return new Uint8Array(await blob.arrayBuffer());
141
+ } catch (error) {
142
+ throw new FormatError(`\u65E0\u6CD5\u628A PDF \u7B2C ${String(pageIndex + 1)} \u9875\u6E32\u67D3\u6210\u56FE\u7247`, {
143
+ cause: error
144
+ });
145
+ }
146
+ };
147
+ return {
148
+ async rasterize(pageIndex, rasterOptions) {
149
+ throwIfAborted(rasterOptions?.signal);
150
+ const cached = cache.get(pageIndex);
151
+ if (cached !== void 0) {
152
+ touch(pageIndex, cached);
153
+ return cached;
154
+ }
155
+ const pending = inFlight.get(pageIndex);
156
+ if (pending !== void 0) return pending;
157
+ const task = renderPage(pageIndex).then((bytes) => {
158
+ remember(pageIndex, bytes);
159
+ return bytes;
160
+ }).finally(() => {
161
+ inFlight.delete(pageIndex);
162
+ });
163
+ inFlight.set(pageIndex, task);
164
+ return task;
165
+ }
166
+ };
167
+ }
168
+
169
+ // src/formats/pdf/open.ts
170
+ async function openPdf(bytes, options) {
171
+ const source = options?.source;
172
+ const loader = options?.loadPdfJs ?? defaultPdfJsLoader;
173
+ throwIfAborted(options?.signal);
174
+ const pdfjs = await loadPdfJs(loader);
175
+ throwIfAborted(options?.signal);
176
+ const { wasmUrl, cMapUrl, standardFontDataUrl, iccUrl } = options?.pdf ?? {};
177
+ const task = pdfjs.getDocument({
178
+ data: new Uint8Array(bytes),
179
+ ...wasmUrl === void 0 ? {} : { wasmUrl },
180
+ ...cMapUrl === void 0 ? {} : { cMapUrl },
181
+ ...standardFontDataUrl === void 0 ? {} : { standardFontDataUrl },
182
+ ...iccUrl === void 0 ? {} : { iccUrl }
183
+ });
184
+ let releaseTask = true;
185
+ try {
186
+ const doc = await openDocument(task, source);
187
+ if (doc.numPages < 1) {
188
+ throw new FormatError(`PDF \u91CC\u4E00\u9875\u90FD\u6CA1\u6709${sourceSuffix(source)}`);
189
+ }
190
+ throwIfAborted(options?.signal);
191
+ const rasterizer = resolveRasterizer(doc, options?.raster);
192
+ const chapters = buildPdfChapters(doc.numPages, { images: rasterizer !== void 0 });
193
+ const metadata = await readMetadata(doc);
194
+ const toc = await mapPdfOutline(doc, pdfPageHref, options);
195
+ const opened = {
196
+ content: createPdfContent(chapters, rasterizer, source),
197
+ book: buildBook(metadata, chapters),
198
+ toc
199
+ };
200
+ releaseTask = rasterizer === void 0;
201
+ return opened;
202
+ } finally {
203
+ if (releaseTask) await task.destroy();
204
+ }
205
+ }
206
+ function resolveRasterizer(doc, raster) {
207
+ if (raster === false) return void 0;
208
+ if (raster !== void 0) return createPdfRasterizer(doc, raster);
209
+ return canRasterize() ? createPdfRasterizer(doc, defaultRasterCanvasFactory) : void 0;
210
+ }
211
+ async function openDocument(task, source) {
212
+ try {
213
+ return await task.promise;
214
+ } catch (error) {
215
+ throw new FormatError(`\u65E0\u6CD5\u89E3\u6790 PDF${sourceSuffix(source)}`, { cause: error });
216
+ }
217
+ }
218
+ function createPdfContent(chapters, rasterizer, source) {
219
+ const documents = new Map(chapters.map((chapter) => [chapter.item.href, chapter.document]));
220
+ const lookupDocument = (name, options) => {
221
+ throwIfAborted(options?.signal);
222
+ const document = documents.get(name);
223
+ if (document === void 0) {
224
+ throw new FormatError(`PDF \u91CC\u6CA1\u6709\u540D\u4E3A "${name}" \u7684\u9875${sourceSuffix(source)}`);
225
+ }
226
+ return document;
227
+ };
228
+ return {
229
+ read: (name, readOptions) => Promise.resolve().then(async () => {
230
+ const image = readPageImage(rasterizer, chapters.length, name, readOptions);
231
+ if (image !== void 0) return image;
232
+ return new TextEncoder().encode(lookupDocument(name, readOptions));
233
+ }),
234
+ readText: (name, readOptions) => Promise.resolve().then(() => lookupDocument(name, readOptions))
235
+ };
236
+ }
237
+ function readPageImage(rasterizer, numPages, name, options) {
238
+ if (rasterizer === void 0) return void 0;
239
+ const index = pdfPageImageIndex(name);
240
+ if (index === void 0 || index >= numPages) return void 0;
241
+ return rasterizer.rasterize(index, options);
242
+ }
243
+ function buildBook(metadata, chapters) {
244
+ return {
245
+ format: "pdf",
246
+ metadata,
247
+ // ⚠️ PDF 的资源(字体 / 图片 / 内容流)都是**对象流**,不是「可按名读取的条目」
248
+ // ⇒ 如实为空,而不是编一份假清单(撞 ADR-0013 的「不猜」)。
249
+ // ⚠️ T3.5b 加了页图**也不改这条**:页图是本包**生成**的(不是文件里的条目),
250
+ // 列进 `resources` 会让 `Book` 多 N 项(O(N) 内存),而 `src/` 里**零消费者**。
251
+ // 需要页图的消费者按 `pdfPageImagePath(i)` 取名即可 —— 那是**唯一**写入点。
252
+ resources: [],
253
+ chapters: chapters.map((chapter) => chapter.item),
254
+ manifest: [],
255
+ spine: [],
256
+ manifestIndex: /* @__PURE__ */ new Map()
257
+ };
258
+ }
259
+ async function readMetadata(doc) {
260
+ const { info } = await doc.getMetadata();
261
+ const fields = info;
262
+ const author = textOf(fields?.["Author"]);
263
+ return {
264
+ title: textOf(fields?.["Title"]),
265
+ creators: author === "" ? [] : [author],
266
+ language: textOf(fields?.["Language"]),
267
+ // PDF 没有必填的标识符字段,`/ID` 是可选且属文件级追踪标识,
268
+ // 不是「这本书是谁」的答案 ⇒ 恒为空串。
269
+ identifier: ""
270
+ };
271
+ }
272
+ function textOf(value) {
273
+ return typeof value === "string" ? value : "";
274
+ }
275
+
276
+ // src/formats/pdf/adapter.ts
277
+ var pdfAdapter = {
278
+ format: "pdf",
279
+ async open(bytes, options) {
280
+ return openPdf(bytes, options);
281
+ }
282
+ };
283
+
284
+ export { pdfAdapter };
@@ -0,0 +1,286 @@
1
+ 'use strict';
2
+
3
+ var chunkG7DLWGBW_cjs = require('./chunk-G7DLWGBW.cjs');
4
+
5
+ // src/formats/pdf/chapters.ts
6
+ var PDF_CHAPTER_MEDIA_TYPE = "application/xhtml+xml";
7
+ var PDF_PAGE_STYLE = "html,body{height:100%;margin:0}img{display:block;max-width:100%;max-height:100%;margin:auto}";
8
+ var NAME_WIDTH = 4;
9
+ function pdfPageImagePath(index) {
10
+ return `page/${String(index).padStart(NAME_WIDTH, "0")}.png`;
11
+ }
12
+ function pdfPageImageReference(index) {
13
+ return `/${pdfPageImagePath(index)}`;
14
+ }
15
+ var PAGE_IMAGE_NAME = /^page\/(\d+)\.png$/;
16
+ function pdfPageImageIndex(name) {
17
+ const digits = PAGE_IMAGE_NAME.exec(name)?.[1];
18
+ if (digits === void 0) return void 0;
19
+ const index = Number(digits);
20
+ return pdfPageImagePath(index) === name ? index : void 0;
21
+ }
22
+ function pdfPageHref(index) {
23
+ return `page/${String(index).padStart(NAME_WIDTH, "0")}.xhtml`;
24
+ }
25
+ function buildPdfChapters(numPages, options) {
26
+ const images = options?.images === true;
27
+ return Array.from({ length: numPages }, (_unused, index) => ({
28
+ item: {
29
+ href: pdfPageHref(index),
30
+ mediaType: PDF_CHAPTER_MEDIA_TYPE,
31
+ linear: true
32
+ },
33
+ document: images ? pageImageDocument(index) : pageDocument(index, numPages)
34
+ }));
35
+ }
36
+ function pageDocument(index, numPages) {
37
+ const page = String(index + 1);
38
+ const total = String(numPages);
39
+ return `<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml"><head><title>\u7B2C ${page} \u9875</title></head><body><p>\u7B2C ${page} \u9875 / \u5171 ${total} \u9875</p><p>\u672C\u9875\u662F PDF \u7684\u56FA\u5B9A\u7248\u5F0F\u5185\u5BB9\uFF0C\u9700\u8981\u5BBF\u4E3B\u6E32\u67D3\u540E\u663E\u793A\u3002</p></body></html>`;
40
+ }
41
+ function pageImageDocument(index) {
42
+ const page = String(index + 1);
43
+ return `<?xml version="1.0" encoding="utf-8"?><html xmlns="http://www.w3.org/1999/xhtml"><head><title>\u7B2C ${page} \u9875</title><style>${PDF_PAGE_STYLE}</style></head><body><img src="${pdfPageImageReference(index)}"/></body></html>`;
44
+ }
45
+
46
+ // src/formats/pdf/label.ts
47
+ function sourceSuffix(label) {
48
+ return label === void 0 ? "" : `\uFF08\u6E90 ${label}\uFF09`;
49
+ }
50
+
51
+ // src/formats/pdf/loader.ts
52
+ var defaultPdfJsLoader = () => import('pdfjs-dist/legacy/build/pdf.mjs');
53
+ async function loadPdfJs(loader) {
54
+ try {
55
+ return await loader();
56
+ } catch (error) {
57
+ throw new chunkG7DLWGBW_cjs.FormatError(
58
+ `PDF \u652F\u6301\u9700\u8981\u53EF\u9009\u4F9D\u8D56 pdfjs-dist\uFF0C\u4F46\u5B83\u672A\u5B89\u88C5\u6216\u52A0\u8F7D\u5931\u8D25\u3002\u8BF7\u5148\u5B89\u88C5\uFF1Anpm i pdfjs-dist`,
59
+ { cause: error }
60
+ );
61
+ }
62
+ }
63
+
64
+ // src/formats/pdf/outline.ts
65
+ async function mapPdfOutline(doc, hrefForPage, options) {
66
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
67
+ const outline = await doc.getOutline();
68
+ if (outline === null) return [];
69
+ return mapNodes(outline);
70
+ async function mapNodes(nodes) {
71
+ const items = [];
72
+ for (const node of nodes) {
73
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
74
+ const index = await pageIndexOf(doc, node.dest);
75
+ const children = await mapNodes(node.items);
76
+ if (index === void 0) {
77
+ items.push(...children);
78
+ continue;
79
+ }
80
+ items.push({ label: node.title, href: hrefForPage(index), children });
81
+ }
82
+ return items;
83
+ }
84
+ }
85
+ async function pageIndexOf(doc, dest) {
86
+ try {
87
+ if (typeof dest === "string") {
88
+ const explicit = await doc.getDestination(dest);
89
+ if (explicit === null) return void 0;
90
+ return await doc.getPageIndex(explicit[0]);
91
+ }
92
+ if (dest === null) return void 0;
93
+ return await doc.getPageIndex(dest[0]);
94
+ } catch {
95
+ return void 0;
96
+ }
97
+ }
98
+
99
+ // src/formats/pdf/raster.ts
100
+ var PDF_RASTER_SCALE = 2;
101
+ var PDF_RASTER_MIME = "image/png";
102
+ var PDF_RASTER_CACHE_BYTES = 16 * 1024 * 1024;
103
+ function canRasterize() {
104
+ return typeof globalThis.OffscreenCanvas === "function";
105
+ }
106
+ var defaultRasterCanvasFactory = (width, height) => {
107
+ const ctor = globalThis.OffscreenCanvas;
108
+ if (ctor === void 0) {
109
+ throw new chunkG7DLWGBW_cjs.FormatError("\u672C\u73AF\u5883\u6CA1\u6709 OffscreenCanvas\uFF0C\u65E0\u6CD5\u628A PDF \u9875\u6E32\u67D3\u6210\u56FE\u7247");
110
+ }
111
+ return new ctor(width, height);
112
+ };
113
+ function createPdfRasterizer(doc, factory, options) {
114
+ const budget = PDF_RASTER_CACHE_BYTES;
115
+ const cache = /* @__PURE__ */ new Map();
116
+ let cachedBytes = 0;
117
+ const inFlight = /* @__PURE__ */ new Map();
118
+ const remember = (pageIndex, bytes) => {
119
+ const size = bytes.byteLength;
120
+ while (cachedBytes + size > budget && cache.size > 0) {
121
+ for (const [oldestPage, oldestBytes] of cache) {
122
+ cache.delete(oldestPage);
123
+ cachedBytes -= oldestBytes.byteLength;
124
+ break;
125
+ }
126
+ }
127
+ if (cachedBytes + size > budget) return;
128
+ cache.set(pageIndex, bytes);
129
+ cachedBytes += size;
130
+ };
131
+ const touch = (pageIndex, bytes) => {
132
+ cache.delete(pageIndex);
133
+ cache.set(pageIndex, bytes);
134
+ };
135
+ const renderPage = async (pageIndex) => {
136
+ try {
137
+ const page = await doc.getPage(pageIndex + 1);
138
+ const viewport = page.getViewport({ scale: PDF_RASTER_SCALE });
139
+ const canvas = factory(Math.ceil(viewport.width), Math.ceil(viewport.height));
140
+ await page.render({ canvas, viewport }).promise;
141
+ const blob = await canvas.convertToBlob({ type: PDF_RASTER_MIME });
142
+ return new Uint8Array(await blob.arrayBuffer());
143
+ } catch (error) {
144
+ throw new chunkG7DLWGBW_cjs.FormatError(`\u65E0\u6CD5\u628A PDF \u7B2C ${String(pageIndex + 1)} \u9875\u6E32\u67D3\u6210\u56FE\u7247`, {
145
+ cause: error
146
+ });
147
+ }
148
+ };
149
+ return {
150
+ async rasterize(pageIndex, rasterOptions) {
151
+ chunkG7DLWGBW_cjs.throwIfAborted(rasterOptions?.signal);
152
+ const cached = cache.get(pageIndex);
153
+ if (cached !== void 0) {
154
+ touch(pageIndex, cached);
155
+ return cached;
156
+ }
157
+ const pending = inFlight.get(pageIndex);
158
+ if (pending !== void 0) return pending;
159
+ const task = renderPage(pageIndex).then((bytes) => {
160
+ remember(pageIndex, bytes);
161
+ return bytes;
162
+ }).finally(() => {
163
+ inFlight.delete(pageIndex);
164
+ });
165
+ inFlight.set(pageIndex, task);
166
+ return task;
167
+ }
168
+ };
169
+ }
170
+
171
+ // src/formats/pdf/open.ts
172
+ async function openPdf(bytes, options) {
173
+ const source = options?.source;
174
+ const loader = options?.loadPdfJs ?? defaultPdfJsLoader;
175
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
176
+ const pdfjs = await loadPdfJs(loader);
177
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
178
+ const { wasmUrl, cMapUrl, standardFontDataUrl, iccUrl } = options?.pdf ?? {};
179
+ const task = pdfjs.getDocument({
180
+ data: new Uint8Array(bytes),
181
+ ...wasmUrl === void 0 ? {} : { wasmUrl },
182
+ ...cMapUrl === void 0 ? {} : { cMapUrl },
183
+ ...standardFontDataUrl === void 0 ? {} : { standardFontDataUrl },
184
+ ...iccUrl === void 0 ? {} : { iccUrl }
185
+ });
186
+ let releaseTask = true;
187
+ try {
188
+ const doc = await openDocument(task, source);
189
+ if (doc.numPages < 1) {
190
+ throw new chunkG7DLWGBW_cjs.FormatError(`PDF \u91CC\u4E00\u9875\u90FD\u6CA1\u6709${sourceSuffix(source)}`);
191
+ }
192
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
193
+ const rasterizer = resolveRasterizer(doc, options?.raster);
194
+ const chapters = buildPdfChapters(doc.numPages, { images: rasterizer !== void 0 });
195
+ const metadata = await readMetadata(doc);
196
+ const toc = await mapPdfOutline(doc, pdfPageHref, options);
197
+ const opened = {
198
+ content: createPdfContent(chapters, rasterizer, source),
199
+ book: buildBook(metadata, chapters),
200
+ toc
201
+ };
202
+ releaseTask = rasterizer === void 0;
203
+ return opened;
204
+ } finally {
205
+ if (releaseTask) await task.destroy();
206
+ }
207
+ }
208
+ function resolveRasterizer(doc, raster) {
209
+ if (raster === false) return void 0;
210
+ if (raster !== void 0) return createPdfRasterizer(doc, raster);
211
+ return canRasterize() ? createPdfRasterizer(doc, defaultRasterCanvasFactory) : void 0;
212
+ }
213
+ async function openDocument(task, source) {
214
+ try {
215
+ return await task.promise;
216
+ } catch (error) {
217
+ throw new chunkG7DLWGBW_cjs.FormatError(`\u65E0\u6CD5\u89E3\u6790 PDF${sourceSuffix(source)}`, { cause: error });
218
+ }
219
+ }
220
+ function createPdfContent(chapters, rasterizer, source) {
221
+ const documents = new Map(chapters.map((chapter) => [chapter.item.href, chapter.document]));
222
+ const lookupDocument = (name, options) => {
223
+ chunkG7DLWGBW_cjs.throwIfAborted(options?.signal);
224
+ const document = documents.get(name);
225
+ if (document === void 0) {
226
+ throw new chunkG7DLWGBW_cjs.FormatError(`PDF \u91CC\u6CA1\u6709\u540D\u4E3A "${name}" \u7684\u9875${sourceSuffix(source)}`);
227
+ }
228
+ return document;
229
+ };
230
+ return {
231
+ read: (name, readOptions) => Promise.resolve().then(async () => {
232
+ const image = readPageImage(rasterizer, chapters.length, name, readOptions);
233
+ if (image !== void 0) return image;
234
+ return new TextEncoder().encode(lookupDocument(name, readOptions));
235
+ }),
236
+ readText: (name, readOptions) => Promise.resolve().then(() => lookupDocument(name, readOptions))
237
+ };
238
+ }
239
+ function readPageImage(rasterizer, numPages, name, options) {
240
+ if (rasterizer === void 0) return void 0;
241
+ const index = pdfPageImageIndex(name);
242
+ if (index === void 0 || index >= numPages) return void 0;
243
+ return rasterizer.rasterize(index, options);
244
+ }
245
+ function buildBook(metadata, chapters) {
246
+ return {
247
+ format: "pdf",
248
+ metadata,
249
+ // ⚠️ PDF 的资源(字体 / 图片 / 内容流)都是**对象流**,不是「可按名读取的条目」
250
+ // ⇒ 如实为空,而不是编一份假清单(撞 ADR-0013 的「不猜」)。
251
+ // ⚠️ T3.5b 加了页图**也不改这条**:页图是本包**生成**的(不是文件里的条目),
252
+ // 列进 `resources` 会让 `Book` 多 N 项(O(N) 内存),而 `src/` 里**零消费者**。
253
+ // 需要页图的消费者按 `pdfPageImagePath(i)` 取名即可 —— 那是**唯一**写入点。
254
+ resources: [],
255
+ chapters: chapters.map((chapter) => chapter.item),
256
+ manifest: [],
257
+ spine: [],
258
+ manifestIndex: /* @__PURE__ */ new Map()
259
+ };
260
+ }
261
+ async function readMetadata(doc) {
262
+ const { info } = await doc.getMetadata();
263
+ const fields = info;
264
+ const author = textOf(fields?.["Author"]);
265
+ return {
266
+ title: textOf(fields?.["Title"]),
267
+ creators: author === "" ? [] : [author],
268
+ language: textOf(fields?.["Language"]),
269
+ // PDF 没有必填的标识符字段,`/ID` 是可选且属文件级追踪标识,
270
+ // 不是「这本书是谁」的答案 ⇒ 恒为空串。
271
+ identifier: ""
272
+ };
273
+ }
274
+ function textOf(value) {
275
+ return typeof value === "string" ? value : "";
276
+ }
277
+
278
+ // src/formats/pdf/adapter.ts
279
+ var pdfAdapter = {
280
+ format: "pdf",
281
+ async open(bytes, options) {
282
+ return openPdf(bytes, options);
283
+ }
284
+ };
285
+
286
+ exports.pdfAdapter = pdfAdapter;