@prose-reader/cbz 1.300.0 → 1.303.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.
@@ -0,0 +1,17 @@
1
+ import { Manifest } from '@prose-reader/shared';
2
+ type SpineItem = Manifest["spineItems"][number];
3
+ export declare const alignSpineItemsForSpreadParity: ({ readingDirection, spineItems, }: {
4
+ readingDirection: Manifest["readingDirection"];
5
+ spineItems: SpineItem[];
6
+ }) => {
7
+ id: string;
8
+ index: number;
9
+ href: string;
10
+ renditionLayout?: `reflowable` | `pre-paginated`;
11
+ renditionFlow?: `scrolled-continuous` | `scrolled-doc` | `paginated` | `auto`;
12
+ progressionWeight?: number;
13
+ pageSpreadLeft?: true | undefined;
14
+ pageSpreadRight?: true | undefined;
15
+ mediaType?: string;
16
+ }[];
17
+ export {};
@@ -0,0 +1,2 @@
1
+ import { StreamerManifestHookFactory } from '@prose-reader/streamer';
2
+ export declare const detectReadingDirectionManifest: StreamerManifestHookFactory;
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.js CHANGED
@@ -1,34 +1,84 @@
1
- import { updateEpubCfiSpineItemref as w, getEpubCfiSpineItemref as b } from "@prose-reader/cfi";
2
- import { detectMimeTypeFromName as c, parseContentType as U, escapeXmlAttributeValue as $ } from "@prose-reader/shared";
3
- const M = 2e3, m = (e) => {
1
+ import { updateEpubCfiSpineItemref as E, getEpubCfiSpineItemref as A } from "@prose-reader/cfi";
2
+ import { createXmlSafeId as y } from "@prose-reader/streamer";
3
+ import { getItemSpreadPosition as d, detectMimeTypeFromName as g, parseContentType as b, escapeXmlAttributeValue as F } from "@prose-reader/shared";
4
+ const T = (e) => e === "left" ? { pageSpreadLeft: !0, pageSpreadRight: void 0 } : { pageSpreadLeft: void 0, pageSpreadRight: !0 }, v = ({
5
+ index: e,
6
+ readingDirection: t
7
+ }) => {
8
+ const r = e % 2 === 0;
9
+ return t === "rtl" ? r ? "right" : "left" : r ? "left" : "right";
10
+ }, L = (e) => e === "rtl" ? "left" : "right", N = ({
11
+ firstItem: e,
12
+ secondItem: t
13
+ }) => {
14
+ if (e === void 0 || t === void 0) return !1;
15
+ const r = d(e), i = d(t);
16
+ return r !== void 0 && i !== void 0 && r !== i;
17
+ }, O = ({
18
+ firstItem: e,
19
+ firstItemIndex: t,
20
+ readingDirection: r,
21
+ secondItem: i
22
+ }) => d(e) === v({
23
+ index: t,
24
+ readingDirection: r
25
+ }) && d(i) === v({
26
+ index: t + 1,
27
+ readingDirection: r
28
+ }), W = ({
29
+ readingDirection: e,
30
+ spineItems: t
31
+ }) => {
32
+ const r = t[0];
33
+ return r === void 0 || d(r) !== void 0 || t.findIndex((n, o) => {
34
+ const a = t[o + 1];
35
+ return !N({
36
+ firstItem: n,
37
+ secondItem: a
38
+ }) || a === void 0 ? !1 : !O({
39
+ firstItem: n,
40
+ firstItemIndex: o,
41
+ readingDirection: e,
42
+ secondItem: a
43
+ });
44
+ }) <= 0 ? t : [
45
+ {
46
+ ...r,
47
+ ...T(
48
+ L(e)
49
+ )
50
+ },
51
+ ...t.slice(1)
52
+ ];
53
+ }, B = 2e3, R = (e) => {
4
54
  const t = Number.parseInt(e, 10);
5
- if (Number.isFinite(t) && !(t < 0 || t > M))
55
+ if (Number.isFinite(t) && !(t < 0 || t > B))
6
56
  return t;
7
- }, C = (e) => {
8
- const t = /(?:^|[\s._(-]|\[)p\s*(\d{1,5})\s*[-_]\s*(?:p\s*)?(\d{1,5})(?=$|[^\d])/i.exec(
57
+ }, D = (e) => {
58
+ const t = /(?:^|[\s._(-]|\[)p\s*(\d{1,5})\s*[-_~]\s*(?:p\s*)?(\d{1,5})(?=$|[^\d])/i.exec(
9
59
  e
10
60
  );
11
- return t || /(?:^|[\s._(]|\[)(0\d{1,4})\s*[-_]\s*(0\d{1,4})(?=$|[^\d])/i.exec(
61
+ return t || /(?:^|[\s._(]|\[)(0\d{1,4})\s*[-_~]\s*(0\d{1,4})(?=$|[^\d])/i.exec(
12
62
  e
13
63
  );
14
- }, A = (e) => {
15
- const t = e.replace(/\.[^.]+$/, ""), r = C(t);
64
+ }, k = (e) => {
65
+ const t = e.replace(/\.[^.]+$/, ""), r = D(t);
16
66
  if (!r) return;
17
- const [, i, o] = r;
18
- if (i === void 0 || o === void 0)
67
+ const [, i, n] = r;
68
+ if (i === void 0 || n === void 0)
19
69
  return;
20
- const n = m(i), s = m(o);
21
- if (!(n === void 0 || s === void 0) && s === n + 1)
70
+ const o = R(i), a = R(n);
71
+ if (!(o === void 0 || a === void 0) && a === o + 1)
22
72
  return {
23
73
  firstPageLabel: i,
24
- secondPageLabel: o
74
+ secondPageLabel: n
25
75
  };
26
- }, P = "__prose-reader__/page-spread", x = "application/xhtml+xml", T = /* @__PURE__ */ new Set([
76
+ }, _ = "__prose-reader__/page-spread", U = "application/xhtml+xml", z = /* @__PURE__ */ new Set([
27
77
  "image/jpg",
28
78
  "image/jpeg",
29
79
  "image/png",
30
80
  "image/webp"
31
- ]), S = (e) => e === void 0 ? !1 : T.has(e), F = (e) => encodeURIComponent(e), O = ({
81
+ ]), I = (e) => e === void 0 ? !1 : z.has(e), H = (e) => encodeURIComponent(e), V = ({
32
82
  baseUrl: e = "",
33
83
  resourcePath: t
34
84
  }) => {
@@ -36,33 +86,33 @@ const M = 2e3, m = (e) => {
36
86
  return encodeURI(t);
37
87
  const r = e ? `${e}${e.endsWith("/") ? "" : "/"}` : "file://";
38
88
  return encodeURI(`${r}${t}`);
39
- }, v = (e) => e.toLowerCase().endsWith(".opf"), L = (e) => e.records.some(
40
- (t) => !t.dir && (v(t.basename) || v(t.uri))
41
- ), N = ({
89
+ }, P = (e) => e.toLowerCase().endsWith(".opf"), X = (e) => e.records.some(
90
+ (t) => !t.dir && (P(t.basename) || P(t.uri))
91
+ ), G = ({
42
92
  cropSide: e,
43
93
  originalUri: t
44
- }) => `${P}/${F(t)}/${e}.xhtml`, W = (e) => e === "left" ? { pageSpreadLeft: !0, pageSpreadRight: void 0 } : { pageSpreadLeft: void 0, pageSpreadRight: !0 }, B = (e) => e === "rtl" ? ["right", "left"] : ["left", "right"], R = ({
94
+ }) => `${_}/${H(t)}/${e}.xhtml`, j = (e) => e === "left" ? { pageSpreadLeft: !0, pageSpreadRight: void 0 } : { pageSpreadLeft: void 0, pageSpreadRight: !0 }, Y = (e) => e === "rtl" ? ["right", "left"] : ["left", "right"], w = ({
45
95
  baseUrl: e,
46
96
  cropSide: t,
47
97
  label: r,
48
98
  originalSpineItem: i,
49
- originalUri: o,
50
- progressionWeight: n
99
+ originalUri: n,
100
+ progressionWeight: o
51
101
  }) => {
52
- const s = N({
102
+ const a = G({
53
103
  cropSide: t,
54
- originalUri: o
104
+ originalUri: n
55
105
  });
56
106
  return {
57
107
  ...i,
58
- id: `${i.id}.${r}`,
59
- href: O({ baseUrl: e, resourcePath: s }),
60
- mediaType: x,
61
- progressionWeight: n,
108
+ id: y(`${i.id}.${r}`),
109
+ href: V({ baseUrl: e, resourcePath: a }),
110
+ mediaType: U,
111
+ progressionWeight: o,
62
112
  renditionLayout: "pre-paginated",
63
- ...W(t)
113
+ ...j(t)
64
114
  };
65
- }, I = ({
115
+ }, x = ({
66
116
  href: e,
67
117
  id: t,
68
118
  mediaType: r
@@ -70,104 +120,107 @@ const M = 2e3, m = (e) => {
70
120
  href: e,
71
121
  id: t,
72
122
  mediaType: r
73
- }), k = ({
123
+ }), Z = ({
74
124
  archive: e,
75
125
  baseUrl: t,
76
126
  spineItem: r
77
127
  }) => {
78
- const i = [r.href, H(r.href)], o = new Set(
79
- i.flatMap((n) => D(n, t))
128
+ const i = [r.href, q(r.href)], n = new Set(
129
+ i.flatMap((o) => K(o, t))
80
130
  );
81
131
  return e.records.find(
82
- (n) => !n.dir && o.has(n.uri)
132
+ (o) => !o.dir && n.has(o.uri)
83
133
  );
84
- }, H = (e) => {
134
+ }, q = (e) => {
85
135
  try {
86
136
  return decodeURI(e);
87
137
  } catch {
88
138
  return e;
89
139
  }
90
- }, V = (e) => e.endsWith("/") ? e : `${e}/`, D = (e, t) => {
140
+ }, J = (e) => e.endsWith("/") ? e : `${e}/`, K = (e, t) => {
91
141
  const r = [e];
92
142
  if (e.startsWith("file://") && r.push(e.slice(7)), t) {
93
- const i = V(t);
143
+ const i = J(t);
94
144
  e.startsWith(i) && r.push(e.slice(i.length));
95
145
  }
96
146
  return r;
97
- }, X = (e) => U(e?.encodingFormat ?? "") || c(e?.basename ?? ""), G = (e) => c(e.uri) || c(e.basename), z = (e) => {
147
+ }, Q = (e) => b(e?.encodingFormat ?? "") || g(e?.basename ?? ""), ee = (e) => g(e.uri) || g(e.basename), te = (e) => {
98
148
  if (e === void 0 || e.dir) return !1;
99
- const t = G(e);
100
- return S(t) ? S(X(e)) : !1;
101
- }, j = ({ archive: e, baseUrl: t }) => async (r) => {
102
- if (L(e)) return r;
103
- const i = [], o = r.spineItems.flatMap((n) => {
104
- const s = k({
149
+ const t = ee(e);
150
+ return I(t) ? I(Q(e)) : !1;
151
+ }, re = ({ archive: e, baseUrl: t }) => async (r) => {
152
+ if (X(e)) return r;
153
+ const i = [], n = r.spineItems.flatMap((a) => {
154
+ const s = Z({
105
155
  archive: e,
106
156
  baseUrl: t,
107
- spineItem: n
157
+ spineItem: a
108
158
  });
109
- if (!z(s))
110
- return [n];
111
- const a = A(s.basename);
112
- if (a === void 0) return [n];
113
- const [_, y] = B(
159
+ if (!te(s))
160
+ return [a];
161
+ const c = k(s.basename);
162
+ if (c === void 0) return [a];
163
+ const [C, $] = Y(
114
164
  r.readingDirection
115
- ), l = n.progressionWeight !== void 0 ? n.progressionWeight / 2 : void 0, h = R({
165
+ ), f = a.progressionWeight !== void 0 ? a.progressionWeight / 2 : void 0, m = w({
116
166
  baseUrl: t,
117
- cropSide: _,
118
- label: a.firstPageLabel,
119
- originalSpineItem: n,
167
+ cropSide: C,
168
+ label: c.firstPageLabel,
169
+ originalSpineItem: a,
120
170
  originalUri: s.uri,
121
- progressionWeight: l
122
- }), f = R({
171
+ progressionWeight: f
172
+ }), S = w({
123
173
  baseUrl: t,
124
- cropSide: y,
125
- label: a.secondPageLabel,
126
- originalSpineItem: n,
174
+ cropSide: $,
175
+ label: c.secondPageLabel,
176
+ originalSpineItem: a,
127
177
  originalUri: s.uri,
128
- progressionWeight: l
178
+ progressionWeight: f
129
179
  });
130
180
  return i.push(
131
- I(h),
132
- I(f)
133
- ), [h, f];
181
+ x(m),
182
+ x(S)
183
+ ), [m, S];
184
+ }), o = W({
185
+ readingDirection: r.readingDirection,
186
+ spineItems: n
134
187
  });
135
- return i.length === 0 ? r : {
188
+ return i.length === 0 && o === n ? r : {
136
189
  ...r,
137
- spineItems: o.map((n, s) => ({
138
- ...n,
190
+ spineItems: o.map((a, s) => ({
191
+ ...a,
139
192
  index: s
140
193
  })),
141
194
  items: [...r.items, ...i]
142
195
  };
143
- }, d = /* @__PURE__ */ new WeakMap(), q = (e) => {
196
+ }, p = /* @__PURE__ */ new WeakMap(), ie = (e) => {
144
197
  try {
145
198
  return decodeURIComponent(e);
146
199
  } catch {
147
200
  return;
148
201
  }
149
- }, p = (e) => {
150
- const t = e.indexOf(`${P}/`);
202
+ }, u = (e) => {
203
+ const t = e.indexOf(`${_}/`);
151
204
  if (t < 0) return;
152
- const i = e.slice(t).split("/"), o = i[2], n = i[3];
153
- if (i.length !== 4 || i[0] !== "__prose-reader__" || i[1] !== "page-spread" || o === void 0 || n === void 0)
205
+ const i = e.slice(t).split("/"), n = i[2], o = i[3];
206
+ if (i.length !== 4 || i[0] !== "__prose-reader__" || i[1] !== "page-spread" || n === void 0 || o === void 0)
154
207
  return;
155
- const s = n.split(".")[0];
156
- if (s !== "left" && s !== "right") return;
157
- const a = q(o);
158
- if (a !== void 0)
208
+ const a = o.split(".")[0];
209
+ if (a !== "left" && a !== "right") return;
210
+ const s = ie(n);
211
+ if (s !== void 0)
159
212
  return {
160
- originalUri: a,
161
- cropSide: s
213
+ originalUri: s,
214
+ cropSide: a
162
215
  };
163
- }, Y = ({
216
+ }, ne = ({
164
217
  cropSide: e,
165
218
  imageHeight: t,
166
219
  imageWidth: r
167
220
  }) => {
168
- const i = Math.floor(r / 2), o = r - i;
169
- return e === "left" ? { x: 0, width: i, height: t } : { x: i, width: o, height: t };
170
- }, Z = (e) => /^https?:\/\//.test(e) ? e : `../../../${encodeURI(e)}`, J = async (e) => {
221
+ const i = Math.floor(r / 2), n = r - i;
222
+ return e === "left" ? { x: 0, width: i, height: t } : { x: i, width: n, height: t };
223
+ }, oe = (e) => /^https?:\/\//.test(e) ? e : `../../../${encodeURI(e)}`, ae = async (e) => {
171
224
  if (typeof createImageBitmap != "function")
172
225
  throw new Error("Page spread XHTML generation requires createImageBitmap");
173
226
  const t = await createImageBitmap(e);
@@ -179,14 +232,14 @@ const M = 2e3, m = (e) => {
179
232
  } finally {
180
233
  t.close();
181
234
  }
182
- }, K = ({
235
+ }, se = ({
183
236
  cropSide: e,
184
237
  imageDimensions: t,
185
238
  originalUri: r
186
239
  }) => {
187
240
  if (t.width < 2)
188
241
  throw new Error("Page spread image is too narrow to split");
189
- const i = Y({
242
+ const i = ne({
190
243
  cropSide: e,
191
244
  imageHeight: t.height,
192
245
  imageWidth: t.width
@@ -216,37 +269,37 @@ const M = 2e3, m = (e) => {
216
269
  </style>
217
270
  </head>
218
271
  <body>
219
- <img src="${$(Z(r))}" alt="" />
272
+ <img src="${F(oe(r))}" alt="" />
220
273
  </body>
221
274
  </html>`;
222
- }, Q = async ({
275
+ }, de = async ({
223
276
  archive: e,
224
277
  resourcePath: t
225
278
  }) => {
226
- const r = p(t);
279
+ const r = u(t);
227
280
  if (r === void 0) return;
228
281
  const i = e.records.find(
229
- (a) => a.uri === r.originalUri && !a.dir
282
+ (s) => s.uri === r.originalUri && !s.dir
230
283
  );
231
284
  if (i === void 0 || i.dir)
232
285
  throw new Error(
233
286
  `no source file found for virtual page spread resourcePath:${t}`
234
287
  );
235
- const o = d.get(e) ?? /* @__PURE__ */ new Map();
236
- d.has(e) || d.set(e, o);
237
- const n = o.get(r.originalUri) ?? await J(await i.blob());
238
- return o.set(r.originalUri, n), {
239
- body: K({
288
+ const n = p.get(e) ?? /* @__PURE__ */ new Map();
289
+ p.has(e) || p.set(e, n);
290
+ const o = n.get(r.originalUri) ?? await ae(await i.blob());
291
+ return n.set(r.originalUri, o), {
292
+ body: se({
240
293
  cropSide: r.cropSide,
241
- imageDimensions: n,
294
+ imageDimensions: o,
242
295
  originalUri: r.originalUri
243
296
  }),
244
297
  params: {
245
- contentType: x
298
+ contentType: U
246
299
  }
247
300
  };
248
- }, ee = ({ archive: e, resourcePath: t }) => async (r) => {
249
- const i = await Q({
301
+ }, ce = ({ archive: e, resourcePath: t }) => async (r) => {
302
+ const i = await de({
250
303
  archive: e,
251
304
  resourcePath: t
252
305
  });
@@ -258,100 +311,113 @@ const M = 2e3, m = (e) => {
258
311
  ...i.params
259
312
  }
260
313
  };
261
- }, g = "vnd.prose-reader.cbz.virtual-spine-id", E = (e) => {
314
+ }, l = "vnd.prose-reader.cbz.virtual-spine-id", M = (e) => {
262
315
  try {
263
316
  return decodeURIComponent(e);
264
317
  } catch {
265
318
  return e;
266
319
  }
267
- }, te = (e) => {
320
+ }, pe = (e) => {
268
321
  try {
269
322
  return decodeURI(e);
270
323
  } catch {
271
324
  return e;
272
325
  }
273
- }, u = (e) => {
274
- const t = p(e) ?? p(te(e));
326
+ }, h = (e) => {
327
+ const t = u(e) ?? u(pe(e));
275
328
  if (t)
276
329
  return {
277
330
  ...t,
278
- originalUri: E(t.originalUri)
331
+ originalUri: M(t.originalUri)
279
332
  };
280
- }, re = (e, t) => {
333
+ }, ge = (e, t) => {
281
334
  const r = /* @__PURE__ */ new Set();
282
335
  let i = 0;
283
- for (const o of e.spineItemsManager.items) {
284
- const n = u(o.item.href);
285
- if (!n) {
336
+ for (const n of e.spineItemsManager.items) {
337
+ const o = h(n.item.href);
338
+ if (!o) {
286
339
  i++;
287
340
  continue;
288
341
  }
289
- if (n.originalUri === t)
342
+ if (o.originalUri === t)
290
343
  return i;
291
- r.has(n.originalUri) || (r.add(n.originalUri), i++);
344
+ r.has(o.originalUri) || (r.add(o.originalUri), i++);
292
345
  }
293
- }, ie = (e, t, r) => {
294
- const i = u(r.href);
346
+ }, ue = (e, t, r) => {
347
+ const i = h(r.href);
295
348
  if (!i) return;
296
- const o = re(
349
+ const n = ge(
297
350
  e,
298
351
  i.originalUri
299
352
  );
300
- if (o !== void 0)
301
- return w(t, {
353
+ if (n !== void 0)
354
+ return E(t, {
302
355
  extensions: {
303
- [g]: encodeURIComponent(r.id)
356
+ [l]: encodeURIComponent(r.id)
304
357
  },
305
- spineId: i.originalUri,
306
- spineIndex: o
358
+ spineId: y(i.originalUri),
359
+ spineIndex: n
307
360
  });
308
- }, ne = (e, t) => {
309
- const r = b(t)?.extensions?.[g];
361
+ }, le = (e, t) => {
362
+ const r = A(t)?.extensions?.[l];
310
363
  if (!r) return;
311
364
  const i = e.spineItemsManager.get(
312
- E(r)
365
+ M(r)
313
366
  );
314
- if (!(!i || !u(i.item.href)))
315
- return w(t, {
367
+ if (!(!i || !h(i.item.href)))
368
+ return E(t, {
316
369
  extensions: {
317
- [g]: void 0
370
+ [l]: void 0
318
371
  },
319
372
  spineId: i.item.id,
320
373
  spineIndex: i.item.index
321
374
  });
322
- }, ae = (e) => (t) => {
375
+ }, Pe = (e) => (t) => {
323
376
  const r = e(t), i = r.hookManager.register(
324
377
  "cfi.afterGenerate",
325
- ({ cfi: s, spineItem: a }) => ie(r, s, a)
326
- ), o = r.hookManager.register(
378
+ ({ cfi: a, spineItem: s }) => ue(r, a, s)
379
+ ), n = r.hookManager.register(
327
380
  "cfi.beforeResolve",
328
- ({ cfi: s }) => ne(r, s)
381
+ ({ cfi: a }) => le(r, a)
329
382
  );
330
383
  return {
331
384
  ...r,
332
385
  __PROSE_READER_ENHANCER_CBZ: !0,
333
386
  destroy: () => {
334
- i(), o(), r.destroy();
387
+ i(), n(), r.destroy();
335
388
  }
336
389
  };
337
- }, de = {
390
+ }, he = /* @__PURE__ */ new Set([
391
+ "application/vnd.comicbook+zip",
392
+ "application/x-cbz"
393
+ ]), fe = (e) => e.toLowerCase().endsWith(".cbz"), me = (e) => {
394
+ const t = b(e.encodingFormat ?? "");
395
+ return !!(t && he.has(t) || fe(e.filename));
396
+ }, Se = ({ archive: e }) => (t) => t.readingDirection !== void 0 || !me(e) ? t : {
397
+ ...t,
398
+ readingDirection: "rtl"
399
+ }, we = {
338
400
  manifest: {
339
- spine: [j]
401
+ content: [Se],
402
+ spine: [re]
340
403
  },
341
- resource: [ee]
404
+ resource: [ce]
342
405
  };
343
406
  export {
344
- P as PAGE_SPREAD_RESOURCE_PREFIX,
345
- x as PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,
346
- N as buildVirtualPageSpreadResourcePath,
347
- ae as cbzEnhancer,
348
- K as createPageSpreadSplitXhtml,
349
- A as detectPageSpreadFromBasename,
350
- z as isPageSpreadSplitSupportedArchiveRecord,
351
- S as isPageSpreadSplitSupportedImage,
352
- j as pageSpreadSplit,
353
- ee as pageSpreadSplitResourceHook,
354
- p as parseVirtualPageSpreadResourcePath,
355
- de as streamerHooks
407
+ he as CBZ_MIME_TYPES,
408
+ _ as PAGE_SPREAD_RESOURCE_PREFIX,
409
+ U as PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,
410
+ G as buildVirtualPageSpreadResourcePath,
411
+ Pe as cbzEnhancer,
412
+ se as createPageSpreadSplitXhtml,
413
+ k as detectPageSpreadFromBasename,
414
+ Se as detectReadingDirectionManifest,
415
+ me as isCbzArchive,
416
+ te as isPageSpreadSplitSupportedArchiveRecord,
417
+ I as isPageSpreadSplitSupportedImage,
418
+ re as pageSpreadSplit,
419
+ ce as pageSpreadSplitResourceHook,
420
+ u as parseVirtualPageSpreadResourcePath,
421
+ we as streamerHooks
356
422
  };
357
423
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/detectPageSpreadFromBasename.ts","../src/pageSpreadSplitManifest.ts","../src/pageSpreadSplitResource.ts","../src/enhancer.ts","../src/streamer.ts"],"sourcesContent":["/**\n * This detector intentionally stays filename-only and conservative.\n *\n * Future improvements should consider archive sequence context before widening\n * detection. For example, a spread such as `p002-003.jpg` is safer to split\n * when neighboring resources make the sequence plausible, like `p001.jpg` and\n * `p004.jpg`.\n */\nexport type DetectedPageSpread = {\n firstPageLabel: string\n secondPageLabel: string\n}\n\nconst MAX_DETECTED_PAGE_NUMBER = 2000\n\nconst numberFromPageLabel = (label: string): number | undefined => {\n const value = Number.parseInt(label, 10)\n\n if (!Number.isFinite(value)) return undefined\n if (value < 0 || value > MAX_DETECTED_PAGE_NUMBER) return undefined\n\n return value\n}\n\nconst detectPageLabelsFromBasename = (basenameWithoutExtension: string) => {\n const explicitPageRangeMatch =\n /(?:^|[\\s._(-]|\\[)p\\s*(\\d{1,5})\\s*[-_]\\s*(?:p\\s*)?(\\d{1,5})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n\n if (explicitPageRangeMatch) return explicitPageRangeMatch\n\n return /(?:^|[\\s._(]|\\[)(0\\d{1,4})\\s*[-_]\\s*(0\\d{1,4})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n}\n\nexport const detectPageSpreadFromBasename = (\n basename: string,\n): DetectedPageSpread | undefined => {\n const basenameWithoutExtension = basename.replace(/\\.[^.]+$/, ``)\n const match = detectPageLabelsFromBasename(basenameWithoutExtension)\n\n if (!match) return undefined\n\n const [, firstPageLabel, secondPageLabel] = match\n\n if (firstPageLabel === undefined || secondPageLabel === undefined) {\n return undefined\n }\n\n const firstPageNumber = numberFromPageLabel(firstPageLabel)\n const secondPageNumber = numberFromPageLabel(secondPageLabel)\n\n if (firstPageNumber === undefined || secondPageNumber === undefined) {\n return undefined\n }\n\n if (secondPageNumber !== firstPageNumber + 1) {\n return undefined\n }\n\n return {\n firstPageLabel,\n secondPageLabel,\n }\n}\n","import {\n detectMimeTypeFromName,\n type Manifest,\n parseContentType,\n} from \"@prose-reader/shared\"\nimport type { Archive } from \"@prose-reader/streamer\"\nimport { detectPageSpreadFromBasename } from \"./detectPageSpreadFromBasename\"\n\nexport {\n type DetectedPageSpread,\n detectPageSpreadFromBasename,\n} from \"./detectPageSpreadFromBasename\"\n\nexport type PageSpreadCropSide = \"left\" | \"right\"\n\nexport type VirtualPageSpreadResource = {\n originalUri: string\n cropSide: PageSpreadCropSide\n}\n\nexport const PAGE_SPREAD_RESOURCE_PREFIX = `__prose-reader__/page-spread`\nexport const PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE = `application/xhtml+xml`\n\nconst supportedImageMediaTypes = new Set([\n `image/jpg`,\n `image/jpeg`,\n `image/png`,\n `image/webp`,\n])\n\nexport const isPageSpreadSplitSupportedImage = (\n mimeType: string | undefined,\n) => {\n if (mimeType === undefined) return false\n\n return supportedImageMediaTypes.has(mimeType)\n}\n\ntype SpineItem = Manifest[\"spineItems\"][number]\ntype ManifestItem = Manifest[\"items\"][number]\ntype ArchiveRecord = Archive[\"records\"][number]\ntype ArchiveFileRecord = Extract<ArchiveRecord, { dir: false }>\n\nconst encodeOriginalUriSegment = (uri: string) => encodeURIComponent(uri)\n\nconst createManifestResourceHref = ({\n baseUrl = ``,\n resourcePath,\n}: {\n baseUrl?: string\n resourcePath: string\n}) => {\n if (!baseUrl && /^https?:\\/\\//.test(resourcePath)) {\n return encodeURI(resourcePath)\n }\n\n const hrefBaseUrl = baseUrl\n ? `${baseUrl}${baseUrl.endsWith(`/`) ? `` : `/`}`\n : `file://`\n\n return encodeURI(`${hrefBaseUrl}${resourcePath}`)\n}\n\nconst hasOpfExtension = (path: string) => path.toLowerCase().endsWith(`.opf`)\n\nconst isArchiveEpub = (archive: Archive) =>\n archive.records.some(\n (file) =>\n !file.dir &&\n (hasOpfExtension(file.basename) || hasOpfExtension(file.uri)),\n )\n\nexport const buildVirtualPageSpreadResourcePath = ({\n cropSide,\n originalUri,\n}: {\n originalUri: string\n cropSide: PageSpreadCropSide\n}) => {\n return `${PAGE_SPREAD_RESOURCE_PREFIX}/${encodeOriginalUriSegment(originalUri)}/${cropSide}.xhtml`\n}\n\nconst spreadPropertiesForSide = (\n side: PageSpreadCropSide,\n): Pick<SpineItem, \"pageSpreadLeft\" | \"pageSpreadRight\"> =>\n side === `left`\n ? { pageSpreadLeft: true, pageSpreadRight: undefined }\n : { pageSpreadLeft: undefined, pageSpreadRight: true }\n\nconst cropSidesInReadingOrder = (\n readingDirection: Manifest[\"readingDirection\"],\n): [PageSpreadCropSide, PageSpreadCropSide] =>\n readingDirection === `rtl` ? [`right`, `left`] : [`left`, `right`]\n\nconst createVirtualSpineItem = ({\n baseUrl,\n cropSide,\n label,\n originalSpineItem,\n originalUri,\n progressionWeight,\n}: {\n baseUrl: string\n originalSpineItem: SpineItem\n originalUri: string\n label: string\n cropSide: PageSpreadCropSide\n progressionWeight: number | undefined\n}): SpineItem => {\n const resourcePath = buildVirtualPageSpreadResourcePath({\n cropSide,\n originalUri,\n })\n\n return {\n ...originalSpineItem,\n id: `${originalSpineItem.id}.${label}`,\n href: createManifestResourceHref({ baseUrl, resourcePath }),\n mediaType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n progressionWeight,\n renditionLayout: `pre-paginated`,\n ...spreadPropertiesForSide(cropSide),\n }\n}\n\nconst createVirtualManifestItem = ({\n href,\n id,\n mediaType,\n}: Pick<ManifestItem, \"href\" | \"id\" | \"mediaType\">): ManifestItem => ({\n href,\n id,\n mediaType,\n})\n\nexport const getArchiveRecordForManifestItem = ({\n archive,\n baseUrl,\n spineItem,\n}: {\n archive: Archive\n baseUrl: string\n spineItem: Manifest[\"spineItems\"][number]\n}): ArchiveRecord | undefined => {\n const hrefCandidates = [spineItem.href, decodeManifestHref(spineItem.href)]\n const resourcePathCandidates = new Set(\n hrefCandidates.flatMap((href) => getResourcePathCandidates(href, baseUrl)),\n )\n\n return archive.records.find(\n (item) => !item.dir && resourcePathCandidates.has(item.uri),\n )\n}\n\nconst decodeManifestHref = (href: string) => {\n try {\n return decodeURI(href)\n } catch {\n return href\n }\n}\n\nconst normalizeBaseUrl = (baseUrl: string) =>\n baseUrl.endsWith(`/`) ? baseUrl : `${baseUrl}/`\n\nconst getResourcePathCandidates = (href: string, baseUrl: string) => {\n const candidates = [href]\n\n if (href.startsWith(`file://`)) {\n candidates.push(href.slice(`file://`.length))\n }\n\n if (baseUrl) {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl)\n\n if (href.startsWith(normalizedBaseUrl)) {\n candidates.push(href.slice(normalizedBaseUrl.length))\n }\n }\n\n return candidates\n}\n\nexport const mediaTypeFromArchiveRecord = (\n record:\n | {\n basename: string\n encodingFormat?: string\n }\n | undefined,\n) =>\n parseContentType(record?.encodingFormat ?? ``) ||\n detectMimeTypeFromName(record?.basename ?? ``)\n\nconst mediaTypeFromArchiveRecordResourcePath = (\n record: Pick<ArchiveRecord, \"basename\" | \"uri\">,\n) =>\n detectMimeTypeFromName(record.uri) || detectMimeTypeFromName(record.basename)\n\nexport const isPageSpreadSplitSupportedArchiveRecord = (\n record: ArchiveRecord | undefined,\n): record is ArchiveFileRecord => {\n if (record === undefined || record.dir) return false\n\n const resourcePathMediaType = mediaTypeFromArchiveRecordResourcePath(record)\n\n if (!isPageSpreadSplitSupportedImage(resourcePathMediaType)) return false\n\n return isPageSpreadSplitSupportedImage(mediaTypeFromArchiveRecord(record))\n}\n\nexport const pageSpreadSplit =\n ({ archive, baseUrl }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n if (isArchiveEpub(archive)) return manifest\n\n const virtualManifestItems: ManifestItem[] = []\n const spineItems = manifest.spineItems.flatMap((spineItem) => {\n const archiveRecord = getArchiveRecordForManifestItem({\n archive,\n baseUrl,\n spineItem,\n })\n\n if (!isPageSpreadSplitSupportedArchiveRecord(archiveRecord)) {\n return [spineItem]\n }\n\n const detected = detectPageSpreadFromBasename(archiveRecord.basename)\n\n if (detected === undefined) return [spineItem]\n\n const [firstCropSide, secondCropSide] = cropSidesInReadingOrder(\n manifest.readingDirection,\n )\n const splitProgressionWeight =\n spineItem.progressionWeight !== undefined\n ? spineItem.progressionWeight / 2\n : undefined\n const firstSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: firstCropSide,\n label: detected.firstPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n const secondSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: secondCropSide,\n label: detected.secondPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n\n virtualManifestItems.push(\n createVirtualManifestItem(firstSpineItem),\n createVirtualManifestItem(secondSpineItem),\n )\n\n return [firstSpineItem, secondSpineItem]\n })\n\n if (virtualManifestItems.length === 0) return manifest\n\n return {\n ...manifest,\n spineItems: spineItems.map((spineItem, index) => ({\n ...spineItem,\n index,\n })),\n items: [...manifest.items, ...virtualManifestItems],\n }\n }\n","import { escapeXmlAttributeValue } from \"@prose-reader/shared\"\nimport type { Archive, HookResource } from \"@prose-reader/streamer\"\nimport {\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\ntype CropRect = {\n x: number\n width: number\n height: number\n}\n\ntype ImageDimensions = {\n width: number\n height: number\n}\n\nconst imageDimensionsCache = new WeakMap<\n Archive,\n Map<string, ImageDimensions>\n>()\n\nconst decodeOriginalUriSegment = (encoded: string): string | undefined => {\n try {\n return decodeURIComponent(encoded)\n } catch {\n return undefined\n }\n}\n\nexport const parseVirtualPageSpreadResourcePath = (\n resourcePath: string,\n): VirtualPageSpreadResource | undefined => {\n const prefixIndex = resourcePath.indexOf(`${PAGE_SPREAD_RESOURCE_PREFIX}/`)\n\n if (prefixIndex < 0) return undefined\n\n const virtualPath = resourcePath.slice(prefixIndex)\n const parts = virtualPath.split(`/`)\n\n const encodedOriginalUri = parts[2]\n const cropFileName = parts[3]\n\n if (\n parts.length !== 4 ||\n parts[0] !== `__prose-reader__` ||\n parts[1] !== `page-spread` ||\n encodedOriginalUri === undefined ||\n cropFileName === undefined\n ) {\n return undefined\n }\n\n const cropSide = cropFileName.split(`.`)[0]\n\n if (cropSide !== `left` && cropSide !== `right`) return undefined\n\n const originalUri = decodeOriginalUriSegment(encodedOriginalUri)\n\n if (originalUri === undefined) return undefined\n\n return {\n originalUri,\n cropSide,\n }\n}\n\nconst cropRectForSide = ({\n cropSide,\n imageHeight,\n imageWidth,\n}: {\n cropSide: PageSpreadCropSide\n imageWidth: number\n imageHeight: number\n}): CropRect => {\n const leftWidth = Math.floor(imageWidth / 2)\n const rightWidth = imageWidth - leftWidth\n\n return cropSide === `left`\n ? { x: 0, width: leftWidth, height: imageHeight }\n : { x: leftWidth, width: rightWidth, height: imageHeight }\n}\n\n/**\n * Since we create a virtual sub path we need to use the relative path to the original image.\n * There is no \"real\" path but the streamer does not need to know that.\n */\nconst getRelativeOriginalImageSrc = (originalUri: string) => {\n if (/^https?:\\/\\//.test(originalUri)) return originalUri\n\n return `../../../${encodeURI(originalUri)}`\n}\n\nconst readImageDimensions = async (source: Blob): Promise<ImageDimensions> => {\n if (typeof createImageBitmap !== `function`) {\n throw new Error(`Page spread XHTML generation requires createImageBitmap`)\n }\n\n const bitmap = await createImageBitmap(source)\n\n try {\n return {\n height: bitmap.height,\n width: bitmap.width,\n }\n } finally {\n bitmap.close()\n }\n}\n\nexport const createPageSpreadSplitXhtml = ({\n cropSide,\n imageDimensions,\n originalUri,\n}: {\n cropSide: PageSpreadCropSide\n imageDimensions: ImageDimensions\n originalUri: string\n}): string => {\n if (imageDimensions.width < 2) {\n throw new Error(`Page spread image is too narrow to split`)\n }\n\n const crop = cropRectForSide({\n cropSide,\n imageHeight: imageDimensions.height,\n imageWidth: imageDimensions.width,\n })\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <meta name=\"viewport\" content=\"width=${crop.width}, height=${crop.height}\" />\n <style>\n html,\n body {\n width: ${crop.width}px;\n height: ${crop.height}px;\n margin: 0;\n overflow: hidden;\n }\n\n img {\n display: block;\n width: ${imageDimensions.width}px;\n height: ${imageDimensions.height}px;\n max-width: none;\n transform: translateX(-${crop.x}px);\n user-select: none;\n -webkit-user-drag: none;\n }\n </style>\n </head>\n <body>\n <img src=\"${escapeXmlAttributeValue(getRelativeOriginalImageSrc(originalUri))}\" alt=\"\" />\n </body>\n</html>`\n}\n\nconst generatePageSpreadSplitResource = async ({\n archive,\n resourcePath,\n}: {\n archive: Archive\n resourcePath: string\n}): Promise<HookResource | undefined> => {\n const virtualResource = parseVirtualPageSpreadResourcePath(resourcePath)\n\n if (virtualResource === undefined) return undefined\n\n const file = archive.records.find(\n (file) => file.uri === virtualResource.originalUri && !file.dir,\n )\n\n if (file === undefined || file.dir) {\n throw new Error(\n `no source file found for virtual page spread resourcePath:${resourcePath}`,\n )\n }\n\n const archiveCache = imageDimensionsCache.get(archive) ?? new Map()\n\n if (!imageDimensionsCache.has(archive)) {\n imageDimensionsCache.set(archive, archiveCache)\n }\n\n const imageDimensions =\n archiveCache.get(virtualResource.originalUri) ??\n (await readImageDimensions(await file.blob()))\n\n archiveCache.set(virtualResource.originalUri, imageDimensions)\n\n const body = createPageSpreadSplitXhtml({\n cropSide: virtualResource.cropSide,\n imageDimensions,\n originalUri: virtualResource.originalUri,\n })\n\n return {\n body,\n params: {\n contentType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n },\n }\n}\n\nexport const pageSpreadSplitResourceHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const pageSpreadResource = await generatePageSpreadSplitResource({\n archive,\n resourcePath,\n })\n\n if (pageSpreadResource === undefined) return resource\n\n return {\n ...resource,\n ...pageSpreadResource,\n params: {\n ...resource.params,\n ...pageSpreadResource.params,\n },\n }\n }\n","import {\n getEpubCfiSpineItemref,\n updateEpubCfiSpineItemref,\n} from \"@prose-reader/cfi\"\nimport type { Reader } from \"@prose-reader/core\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { parseVirtualPageSpreadResourcePath } from \"./pageSpreadSplitResource\"\n\nconst VIRTUAL_SPINE_ID_EXTENSION = \"vnd.prose-reader.cbz.virtual-spine-id\"\n\ntype SpineItem = Manifest[\"spineItems\"][number]\n\nexport type CbzEnhancerAPI = {\n __PROSE_READER_ENHANCER_CBZ: true\n}\n\nconst decodeURIComponentSafe = (value: string) => {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\nconst decodeURISafe = (value: string) => {\n try {\n return decodeURI(value)\n } catch {\n return value\n }\n}\n\nconst parseVirtualPageSpreadFromHref = (href: string) => {\n const virtualResource =\n parseVirtualPageSpreadResourcePath(href) ??\n parseVirtualPageSpreadResourcePath(decodeURISafe(href))\n\n if (!virtualResource) return undefined\n\n return {\n ...virtualResource,\n originalUri: decodeURIComponentSafe(virtualResource.originalUri),\n }\n}\n\nconst getOriginalSpineIndex = (reader: Reader, originalUri: string) => {\n const seenVirtualOriginalUris = new Set<string>()\n let originalSpineIndex = 0\n\n for (const spineItem of reader.spineItemsManager.items) {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.item.href)\n\n if (!virtualResource) {\n originalSpineIndex++\n continue\n }\n\n if (virtualResource.originalUri === originalUri) {\n return originalSpineIndex\n }\n\n if (!seenVirtualOriginalUris.has(virtualResource.originalUri)) {\n seenVirtualOriginalUris.add(virtualResource.originalUri)\n originalSpineIndex++\n }\n }\n\n return undefined\n}\n\nconst restoreOriginalSpineReference = (\n reader: Reader,\n cfi: string,\n spineItem: SpineItem,\n) => {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.href)\n\n if (!virtualResource) return undefined\n\n const originalSpineIndex = getOriginalSpineIndex(\n reader,\n virtualResource.originalUri,\n )\n\n if (originalSpineIndex === undefined) return undefined\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: encodeURIComponent(spineItem.id),\n },\n spineId: virtualResource.originalUri,\n spineIndex: originalSpineIndex,\n })\n}\n\nconst restoreVirtualSpineReference = (reader: Reader, cfi: string) => {\n const virtualSpineId =\n getEpubCfiSpineItemref(cfi)?.extensions?.[VIRTUAL_SPINE_ID_EXTENSION]\n\n if (!virtualSpineId) return undefined\n\n const virtualSpineItem = reader.spineItemsManager.get(\n decodeURIComponentSafe(virtualSpineId),\n )\n\n if (\n !virtualSpineItem ||\n !parseVirtualPageSpreadFromHref(virtualSpineItem.item.href)\n ) {\n return undefined\n }\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: undefined,\n },\n spineId: virtualSpineItem.item.id,\n spineIndex: virtualSpineItem.item.index,\n })\n}\n\nexport const cbzEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & CbzEnhancerAPI => {\n const reader = next(options)\n\n const unregisterGenerateHook = reader.hookManager.register(\n \"cfi.afterGenerate\",\n ({ cfi, spineItem }) =>\n restoreOriginalSpineReference(reader, cfi, spineItem),\n )\n\n const unregisterResolveHook = reader.hookManager.register(\n \"cfi.beforeResolve\",\n ({ cfi }) => restoreVirtualSpineReference(reader, cfi),\n )\n\n const destroy = () => {\n unregisterGenerateHook()\n unregisterResolveHook()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_CBZ: true,\n destroy,\n }\n }\n","import type {\n StreamerManifestHookFactory,\n StreamerResourceHookFactory,\n} from \"@prose-reader/streamer\"\nimport { pageSpreadSplit } from \"./pageSpreadSplitManifest\"\nimport { pageSpreadSplitResourceHook } from \"./pageSpreadSplitResource\"\n\nexport const streamerHooks: {\n manifest: {\n spine: StreamerManifestHookFactory[]\n }\n resource: StreamerResourceHookFactory[]\n} = {\n manifest: {\n spine: [pageSpreadSplit],\n },\n resource: [pageSpreadSplitResourceHook],\n}\n\nexport {\n buildVirtualPageSpreadResourcePath,\n detectPageSpreadFromBasename,\n isPageSpreadSplitSupportedArchiveRecord,\n isPageSpreadSplitSupportedImage,\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n pageSpreadSplit,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\nexport {\n createPageSpreadSplitXhtml,\n pageSpreadSplitResourceHook,\n parseVirtualPageSpreadResourcePath,\n} from \"./pageSpreadSplitResource\"\n"],"names":["MAX_DETECTED_PAGE_NUMBER","numberFromPageLabel","label","value","detectPageLabelsFromBasename","basenameWithoutExtension","explicitPageRangeMatch","detectPageSpreadFromBasename","basename","match","firstPageLabel","secondPageLabel","firstPageNumber","secondPageNumber","PAGE_SPREAD_RESOURCE_PREFIX","PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE","supportedImageMediaTypes","isPageSpreadSplitSupportedImage","mimeType","encodeOriginalUriSegment","uri","createManifestResourceHref","baseUrl","resourcePath","hrefBaseUrl","hasOpfExtension","path","isArchiveEpub","archive","file","buildVirtualPageSpreadResourcePath","cropSide","originalUri","spreadPropertiesForSide","side","cropSidesInReadingOrder","readingDirection","createVirtualSpineItem","originalSpineItem","progressionWeight","createVirtualManifestItem","href","id","mediaType","getArchiveRecordForManifestItem","spineItem","hrefCandidates","decodeManifestHref","resourcePathCandidates","getResourcePathCandidates","item","normalizeBaseUrl","candidates","normalizedBaseUrl","mediaTypeFromArchiveRecord","record","parseContentType","detectMimeTypeFromName","mediaTypeFromArchiveRecordResourcePath","isPageSpreadSplitSupportedArchiveRecord","resourcePathMediaType","pageSpreadSplit","manifest","virtualManifestItems","spineItems","archiveRecord","detected","firstCropSide","secondCropSide","splitProgressionWeight","firstSpineItem","secondSpineItem","index","imageDimensionsCache","decodeOriginalUriSegment","encoded","parseVirtualPageSpreadResourcePath","prefixIndex","parts","encodedOriginalUri","cropFileName","cropRectForSide","imageHeight","imageWidth","leftWidth","rightWidth","getRelativeOriginalImageSrc","readImageDimensions","source","bitmap","createPageSpreadSplitXhtml","imageDimensions","crop","escapeXmlAttributeValue","generatePageSpreadSplitResource","virtualResource","archiveCache","pageSpreadSplitResourceHook","resource","pageSpreadResource","VIRTUAL_SPINE_ID_EXTENSION","decodeURIComponentSafe","decodeURISafe","parseVirtualPageSpreadFromHref","getOriginalSpineIndex","reader","seenVirtualOriginalUris","originalSpineIndex","restoreOriginalSpineReference","cfi","updateEpubCfiSpineItemref","restoreVirtualSpineReference","virtualSpineId","getEpubCfiSpineItemref","virtualSpineItem","cbzEnhancer","next","options","unregisterGenerateHook","unregisterResolveHook","streamerHooks"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/alignSpineItemsForSpreadParity.ts","../src/detectPageSpreadFromBasename.ts","../src/pageSpreadSplitManifest.ts","../src/pageSpreadSplitResource.ts","../src/enhancer.ts","../src/isCbzArchive.ts","../src/detectReadingDirectionManifest.ts","../src/streamer.ts"],"sourcesContent":["import { getItemSpreadPosition, type Manifest } from \"@prose-reader/shared\"\n\ntype SpineItem = Manifest[\"spineItems\"][number]\ntype PageSpreadSide = \"left\" | \"right\"\n\nconst spreadPropertiesForSide = (\n side: PageSpreadSide,\n): Pick<SpineItem, \"pageSpreadLeft\" | \"pageSpreadRight\"> =>\n side === `left`\n ? { pageSpreadLeft: true, pageSpreadRight: undefined }\n : { pageSpreadLeft: undefined, pageSpreadRight: true }\n\nconst naturalSpreadSideAtIndex = ({\n index,\n readingDirection,\n}: {\n index: number\n readingDirection: Manifest[\"readingDirection\"]\n}): PageSpreadSide => {\n const isEvenSlot = index % 2 === 0\n\n return readingDirection === `rtl`\n ? isEvenSlot\n ? `right`\n : `left`\n : isEvenSlot\n ? `left`\n : `right`\n}\n\nconst openingPageSideForParityShift = (\n readingDirection: Manifest[\"readingDirection\"],\n): PageSpreadSide => (readingDirection === `rtl` ? `left` : `right`)\n\nconst isSpreadPair = ({\n firstItem,\n secondItem,\n}: {\n firstItem: SpineItem | undefined\n secondItem: SpineItem | undefined\n}) => {\n if (firstItem === undefined || secondItem === undefined) return false\n\n const firstSide = getItemSpreadPosition(firstItem)\n const secondSide = getItemSpreadPosition(secondItem)\n\n return (\n firstSide !== undefined &&\n secondSide !== undefined &&\n firstSide !== secondSide\n )\n}\n\nconst isSpreadPairNaturallyAligned = ({\n firstItem,\n firstItemIndex,\n readingDirection,\n secondItem,\n}: {\n firstItem: SpineItem\n firstItemIndex: number\n readingDirection: Manifest[\"readingDirection\"]\n secondItem: SpineItem\n}) =>\n getItemSpreadPosition(firstItem) ===\n naturalSpreadSideAtIndex({\n index: firstItemIndex,\n readingDirection,\n }) &&\n getItemSpreadPosition(secondItem) ===\n naturalSpreadSideAtIndex({\n index: firstItemIndex + 1,\n readingDirection,\n })\n\nexport const alignSpineItemsForSpreadParity = ({\n readingDirection,\n spineItems,\n}: {\n readingDirection: Manifest[\"readingDirection\"]\n spineItems: SpineItem[]\n}) => {\n const openingItem = spineItems[0]\n\n if (\n openingItem === undefined ||\n getItemSpreadPosition(openingItem) !== undefined\n ) {\n return spineItems\n }\n\n const misalignedPairIndex = spineItems.findIndex((firstItem, index) => {\n const secondItem = spineItems[index + 1]\n\n if (\n !isSpreadPair({\n firstItem,\n secondItem,\n })\n ) {\n return false\n }\n\n if (secondItem === undefined) return false\n\n return !isSpreadPairNaturallyAligned({\n firstItem,\n firstItemIndex: index,\n readingDirection,\n secondItem,\n })\n })\n\n if (misalignedPairIndex <= 0) return spineItems\n\n return [\n {\n ...openingItem,\n ...spreadPropertiesForSide(\n openingPageSideForParityShift(readingDirection),\n ),\n },\n ...spineItems.slice(1),\n ]\n}\n","/**\n * This detector intentionally stays filename-only and conservative.\n *\n * Future improvements should consider archive sequence context before widening\n * detection. For example, a spread such as `p002-003.jpg` is safer to split\n * when neighboring resources make the sequence plausible, like `p001.jpg` and\n * `p004.jpg`.\n */\nexport type DetectedPageSpread = {\n firstPageLabel: string\n secondPageLabel: string\n}\n\nconst MAX_DETECTED_PAGE_NUMBER = 2000\n\nconst numberFromPageLabel = (label: string): number | undefined => {\n const value = Number.parseInt(label, 10)\n\n if (!Number.isFinite(value)) return undefined\n if (value < 0 || value > MAX_DETECTED_PAGE_NUMBER) return undefined\n\n return value\n}\n\nconst detectPageLabelsFromBasename = (basenameWithoutExtension: string) => {\n const explicitPageRangeMatch =\n /(?:^|[\\s._(-]|\\[)p\\s*(\\d{1,5})\\s*[-_~]\\s*(?:p\\s*)?(\\d{1,5})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n\n if (explicitPageRangeMatch) return explicitPageRangeMatch\n\n return /(?:^|[\\s._(]|\\[)(0\\d{1,4})\\s*[-_~]\\s*(0\\d{1,4})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n}\n\nexport const detectPageSpreadFromBasename = (\n basename: string,\n): DetectedPageSpread | undefined => {\n const basenameWithoutExtension = basename.replace(/\\.[^.]+$/, ``)\n const match = detectPageLabelsFromBasename(basenameWithoutExtension)\n\n if (!match) return undefined\n\n const [, firstPageLabel, secondPageLabel] = match\n\n if (firstPageLabel === undefined || secondPageLabel === undefined) {\n return undefined\n }\n\n const firstPageNumber = numberFromPageLabel(firstPageLabel)\n const secondPageNumber = numberFromPageLabel(secondPageLabel)\n\n if (firstPageNumber === undefined || secondPageNumber === undefined) {\n return undefined\n }\n\n if (secondPageNumber !== firstPageNumber + 1) {\n return undefined\n }\n\n return {\n firstPageLabel,\n secondPageLabel,\n }\n}\n","import {\n detectMimeTypeFromName,\n type Manifest,\n parseContentType,\n} from \"@prose-reader/shared\"\nimport { type Archive, createXmlSafeId } from \"@prose-reader/streamer\"\nimport { alignSpineItemsForSpreadParity } from \"./alignSpineItemsForSpreadParity\"\nimport { detectPageSpreadFromBasename } from \"./detectPageSpreadFromBasename\"\n\nexport {\n type DetectedPageSpread,\n detectPageSpreadFromBasename,\n} from \"./detectPageSpreadFromBasename\"\n\nexport type PageSpreadCropSide = \"left\" | \"right\"\n\nexport type VirtualPageSpreadResource = {\n originalUri: string\n cropSide: PageSpreadCropSide\n}\n\nexport const PAGE_SPREAD_RESOURCE_PREFIX = `__prose-reader__/page-spread`\nexport const PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE = `application/xhtml+xml`\n\nconst supportedImageMediaTypes = new Set([\n `image/jpg`,\n `image/jpeg`,\n `image/png`,\n `image/webp`,\n])\n\nexport const isPageSpreadSplitSupportedImage = (\n mimeType: string | undefined,\n) => {\n if (mimeType === undefined) return false\n\n return supportedImageMediaTypes.has(mimeType)\n}\n\ntype SpineItem = Manifest[\"spineItems\"][number]\ntype ManifestItem = Manifest[\"items\"][number]\ntype ArchiveRecord = Archive[\"records\"][number]\ntype ArchiveFileRecord = Extract<ArchiveRecord, { dir: false }>\n\nconst encodeOriginalUriSegment = (uri: string) => encodeURIComponent(uri)\n\nconst createManifestResourceHref = ({\n baseUrl = ``,\n resourcePath,\n}: {\n baseUrl?: string\n resourcePath: string\n}) => {\n if (!baseUrl && /^https?:\\/\\//.test(resourcePath)) {\n return encodeURI(resourcePath)\n }\n\n const hrefBaseUrl = baseUrl\n ? `${baseUrl}${baseUrl.endsWith(`/`) ? `` : `/`}`\n : `file://`\n\n return encodeURI(`${hrefBaseUrl}${resourcePath}`)\n}\n\nconst hasOpfExtension = (path: string) => path.toLowerCase().endsWith(`.opf`)\n\nconst isArchiveEpub = (archive: Archive) =>\n archive.records.some(\n (file) =>\n !file.dir &&\n (hasOpfExtension(file.basename) || hasOpfExtension(file.uri)),\n )\n\nexport const buildVirtualPageSpreadResourcePath = ({\n cropSide,\n originalUri,\n}: {\n originalUri: string\n cropSide: PageSpreadCropSide\n}) => {\n return `${PAGE_SPREAD_RESOURCE_PREFIX}/${encodeOriginalUriSegment(originalUri)}/${cropSide}.xhtml`\n}\n\nconst spreadPropertiesForSide = (\n side: PageSpreadCropSide,\n): Pick<SpineItem, \"pageSpreadLeft\" | \"pageSpreadRight\"> =>\n side === `left`\n ? { pageSpreadLeft: true, pageSpreadRight: undefined }\n : { pageSpreadLeft: undefined, pageSpreadRight: true }\n\nconst cropSidesInReadingOrder = (\n readingDirection: Manifest[\"readingDirection\"],\n): [PageSpreadCropSide, PageSpreadCropSide] =>\n readingDirection === `rtl` ? [`right`, `left`] : [`left`, `right`]\n\nconst createVirtualSpineItem = ({\n baseUrl,\n cropSide,\n label,\n originalSpineItem,\n originalUri,\n progressionWeight,\n}: {\n baseUrl: string\n originalSpineItem: SpineItem\n originalUri: string\n label: string\n cropSide: PageSpreadCropSide\n progressionWeight: number | undefined\n}): SpineItem => {\n const resourcePath = buildVirtualPageSpreadResourcePath({\n cropSide,\n originalUri,\n })\n\n return {\n ...originalSpineItem,\n id: createXmlSafeId(`${originalSpineItem.id}.${label}`),\n href: createManifestResourceHref({ baseUrl, resourcePath }),\n mediaType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n progressionWeight,\n renditionLayout: `pre-paginated`,\n ...spreadPropertiesForSide(cropSide),\n }\n}\n\nconst createVirtualManifestItem = ({\n href,\n id,\n mediaType,\n}: Pick<ManifestItem, \"href\" | \"id\" | \"mediaType\">): ManifestItem => ({\n href,\n id,\n mediaType,\n})\n\nexport const getArchiveRecordForManifestItem = ({\n archive,\n baseUrl,\n spineItem,\n}: {\n archive: Archive\n baseUrl: string\n spineItem: Manifest[\"spineItems\"][number]\n}): ArchiveRecord | undefined => {\n const hrefCandidates = [spineItem.href, decodeManifestHref(spineItem.href)]\n const resourcePathCandidates = new Set(\n hrefCandidates.flatMap((href) => getResourcePathCandidates(href, baseUrl)),\n )\n\n return archive.records.find(\n (item) => !item.dir && resourcePathCandidates.has(item.uri),\n )\n}\n\nconst decodeManifestHref = (href: string) => {\n try {\n return decodeURI(href)\n } catch {\n return href\n }\n}\n\nconst normalizeBaseUrl = (baseUrl: string) =>\n baseUrl.endsWith(`/`) ? baseUrl : `${baseUrl}/`\n\nconst getResourcePathCandidates = (href: string, baseUrl: string) => {\n const candidates = [href]\n\n if (href.startsWith(`file://`)) {\n candidates.push(href.slice(`file://`.length))\n }\n\n if (baseUrl) {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl)\n\n if (href.startsWith(normalizedBaseUrl)) {\n candidates.push(href.slice(normalizedBaseUrl.length))\n }\n }\n\n return candidates\n}\n\nexport const mediaTypeFromArchiveRecord = (\n record:\n | {\n basename: string\n encodingFormat?: string\n }\n | undefined,\n) =>\n parseContentType(record?.encodingFormat ?? ``) ||\n detectMimeTypeFromName(record?.basename ?? ``)\n\nconst mediaTypeFromArchiveRecordResourcePath = (\n record: Pick<ArchiveRecord, \"basename\" | \"uri\">,\n) =>\n detectMimeTypeFromName(record.uri) || detectMimeTypeFromName(record.basename)\n\nexport const isPageSpreadSplitSupportedArchiveRecord = (\n record: ArchiveRecord | undefined,\n): record is ArchiveFileRecord => {\n if (record === undefined || record.dir) return false\n\n const resourcePathMediaType = mediaTypeFromArchiveRecordResourcePath(record)\n\n if (!isPageSpreadSplitSupportedImage(resourcePathMediaType)) return false\n\n return isPageSpreadSplitSupportedImage(mediaTypeFromArchiveRecord(record))\n}\n\nexport const pageSpreadSplit =\n ({ archive, baseUrl }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n if (isArchiveEpub(archive)) return manifest\n\n const virtualManifestItems: ManifestItem[] = []\n const spineItems = manifest.spineItems.flatMap((spineItem) => {\n const archiveRecord = getArchiveRecordForManifestItem({\n archive,\n baseUrl,\n spineItem,\n })\n\n if (!isPageSpreadSplitSupportedArchiveRecord(archiveRecord)) {\n return [spineItem]\n }\n\n const detected = detectPageSpreadFromBasename(archiveRecord.basename)\n\n if (detected === undefined) return [spineItem]\n\n const [firstCropSide, secondCropSide] = cropSidesInReadingOrder(\n manifest.readingDirection,\n )\n const splitProgressionWeight =\n spineItem.progressionWeight !== undefined\n ? spineItem.progressionWeight / 2\n : undefined\n const firstSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: firstCropSide,\n label: detected.firstPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n const secondSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: secondCropSide,\n label: detected.secondPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n\n virtualManifestItems.push(\n createVirtualManifestItem(firstSpineItem),\n createVirtualManifestItem(secondSpineItem),\n )\n\n return [firstSpineItem, secondSpineItem]\n })\n\n const alignedSpineItems = alignSpineItemsForSpreadParity({\n readingDirection: manifest.readingDirection,\n spineItems,\n })\n\n if (virtualManifestItems.length === 0 && alignedSpineItems === spineItems) {\n return manifest\n }\n\n return {\n ...manifest,\n spineItems: alignedSpineItems.map((spineItem, index) => ({\n ...spineItem,\n index,\n })),\n items: [...manifest.items, ...virtualManifestItems],\n }\n }\n","import { escapeXmlAttributeValue } from \"@prose-reader/shared\"\nimport type { Archive, HookResource } from \"@prose-reader/streamer\"\nimport {\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\ntype CropRect = {\n x: number\n width: number\n height: number\n}\n\ntype ImageDimensions = {\n width: number\n height: number\n}\n\nconst imageDimensionsCache = new WeakMap<\n Archive,\n Map<string, ImageDimensions>\n>()\n\nconst decodeOriginalUriSegment = (encoded: string): string | undefined => {\n try {\n return decodeURIComponent(encoded)\n } catch {\n return undefined\n }\n}\n\nexport const parseVirtualPageSpreadResourcePath = (\n resourcePath: string,\n): VirtualPageSpreadResource | undefined => {\n const prefixIndex = resourcePath.indexOf(`${PAGE_SPREAD_RESOURCE_PREFIX}/`)\n\n if (prefixIndex < 0) return undefined\n\n const virtualPath = resourcePath.slice(prefixIndex)\n const parts = virtualPath.split(`/`)\n\n const encodedOriginalUri = parts[2]\n const cropFileName = parts[3]\n\n if (\n parts.length !== 4 ||\n parts[0] !== `__prose-reader__` ||\n parts[1] !== `page-spread` ||\n encodedOriginalUri === undefined ||\n cropFileName === undefined\n ) {\n return undefined\n }\n\n const cropSide = cropFileName.split(`.`)[0]\n\n if (cropSide !== `left` && cropSide !== `right`) return undefined\n\n const originalUri = decodeOriginalUriSegment(encodedOriginalUri)\n\n if (originalUri === undefined) return undefined\n\n return {\n originalUri,\n cropSide,\n }\n}\n\nconst cropRectForSide = ({\n cropSide,\n imageHeight,\n imageWidth,\n}: {\n cropSide: PageSpreadCropSide\n imageWidth: number\n imageHeight: number\n}): CropRect => {\n const leftWidth = Math.floor(imageWidth / 2)\n const rightWidth = imageWidth - leftWidth\n\n return cropSide === `left`\n ? { x: 0, width: leftWidth, height: imageHeight }\n : { x: leftWidth, width: rightWidth, height: imageHeight }\n}\n\n/**\n * Since we create a virtual sub path we need to use the relative path to the original image.\n * There is no \"real\" path but the streamer does not need to know that.\n */\nconst getRelativeOriginalImageSrc = (originalUri: string) => {\n if (/^https?:\\/\\//.test(originalUri)) return originalUri\n\n return `../../../${encodeURI(originalUri)}`\n}\n\nconst readImageDimensions = async (source: Blob): Promise<ImageDimensions> => {\n if (typeof createImageBitmap !== `function`) {\n throw new Error(`Page spread XHTML generation requires createImageBitmap`)\n }\n\n const bitmap = await createImageBitmap(source)\n\n try {\n return {\n height: bitmap.height,\n width: bitmap.width,\n }\n } finally {\n bitmap.close()\n }\n}\n\nexport const createPageSpreadSplitXhtml = ({\n cropSide,\n imageDimensions,\n originalUri,\n}: {\n cropSide: PageSpreadCropSide\n imageDimensions: ImageDimensions\n originalUri: string\n}): string => {\n if (imageDimensions.width < 2) {\n throw new Error(`Page spread image is too narrow to split`)\n }\n\n const crop = cropRectForSide({\n cropSide,\n imageHeight: imageDimensions.height,\n imageWidth: imageDimensions.width,\n })\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <meta name=\"viewport\" content=\"width=${crop.width}, height=${crop.height}\" />\n <style>\n html,\n body {\n width: ${crop.width}px;\n height: ${crop.height}px;\n margin: 0;\n overflow: hidden;\n }\n\n img {\n display: block;\n width: ${imageDimensions.width}px;\n height: ${imageDimensions.height}px;\n max-width: none;\n transform: translateX(-${crop.x}px);\n user-select: none;\n -webkit-user-drag: none;\n }\n </style>\n </head>\n <body>\n <img src=\"${escapeXmlAttributeValue(getRelativeOriginalImageSrc(originalUri))}\" alt=\"\" />\n </body>\n</html>`\n}\n\nconst generatePageSpreadSplitResource = async ({\n archive,\n resourcePath,\n}: {\n archive: Archive\n resourcePath: string\n}): Promise<HookResource | undefined> => {\n const virtualResource = parseVirtualPageSpreadResourcePath(resourcePath)\n\n if (virtualResource === undefined) return undefined\n\n const file = archive.records.find(\n (file) => file.uri === virtualResource.originalUri && !file.dir,\n )\n\n if (file === undefined || file.dir) {\n throw new Error(\n `no source file found for virtual page spread resourcePath:${resourcePath}`,\n )\n }\n\n const archiveCache = imageDimensionsCache.get(archive) ?? new Map()\n\n if (!imageDimensionsCache.has(archive)) {\n imageDimensionsCache.set(archive, archiveCache)\n }\n\n const imageDimensions =\n archiveCache.get(virtualResource.originalUri) ??\n (await readImageDimensions(await file.blob()))\n\n archiveCache.set(virtualResource.originalUri, imageDimensions)\n\n const body = createPageSpreadSplitXhtml({\n cropSide: virtualResource.cropSide,\n imageDimensions,\n originalUri: virtualResource.originalUri,\n })\n\n return {\n body,\n params: {\n contentType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n },\n }\n}\n\nexport const pageSpreadSplitResourceHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const pageSpreadResource = await generatePageSpreadSplitResource({\n archive,\n resourcePath,\n })\n\n if (pageSpreadResource === undefined) return resource\n\n return {\n ...resource,\n ...pageSpreadResource,\n params: {\n ...resource.params,\n ...pageSpreadResource.params,\n },\n }\n }\n","import {\n getEpubCfiSpineItemref,\n updateEpubCfiSpineItemref,\n} from \"@prose-reader/cfi\"\nimport type { Reader } from \"@prose-reader/core\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { createXmlSafeId } from \"@prose-reader/streamer\"\nimport { parseVirtualPageSpreadResourcePath } from \"./pageSpreadSplitResource\"\n\nconst VIRTUAL_SPINE_ID_EXTENSION = \"vnd.prose-reader.cbz.virtual-spine-id\"\n\ntype SpineItem = Manifest[\"spineItems\"][number]\n\nexport type CbzEnhancerAPI = {\n __PROSE_READER_ENHANCER_CBZ: true\n}\n\nconst decodeURIComponentSafe = (value: string) => {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\nconst decodeURISafe = (value: string) => {\n try {\n return decodeURI(value)\n } catch {\n return value\n }\n}\n\nconst parseVirtualPageSpreadFromHref = (href: string) => {\n const virtualResource =\n parseVirtualPageSpreadResourcePath(href) ??\n parseVirtualPageSpreadResourcePath(decodeURISafe(href))\n\n if (!virtualResource) return undefined\n\n return {\n ...virtualResource,\n originalUri: decodeURIComponentSafe(virtualResource.originalUri),\n }\n}\n\nconst getOriginalSpineIndex = (reader: Reader, originalUri: string) => {\n const seenVirtualOriginalUris = new Set<string>()\n let originalSpineIndex = 0\n\n for (const spineItem of reader.spineItemsManager.items) {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.item.href)\n\n if (!virtualResource) {\n originalSpineIndex++\n continue\n }\n\n if (virtualResource.originalUri === originalUri) {\n return originalSpineIndex\n }\n\n if (!seenVirtualOriginalUris.has(virtualResource.originalUri)) {\n seenVirtualOriginalUris.add(virtualResource.originalUri)\n originalSpineIndex++\n }\n }\n\n return undefined\n}\n\nconst restoreOriginalSpineReference = (\n reader: Reader,\n cfi: string,\n spineItem: SpineItem,\n) => {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.href)\n\n if (!virtualResource) return undefined\n\n const originalSpineIndex = getOriginalSpineIndex(\n reader,\n virtualResource.originalUri,\n )\n\n if (originalSpineIndex === undefined) return undefined\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: encodeURIComponent(spineItem.id),\n },\n spineId: createXmlSafeId(virtualResource.originalUri),\n spineIndex: originalSpineIndex,\n })\n}\n\nconst restoreVirtualSpineReference = (reader: Reader, cfi: string) => {\n const virtualSpineId =\n getEpubCfiSpineItemref(cfi)?.extensions?.[VIRTUAL_SPINE_ID_EXTENSION]\n\n if (!virtualSpineId) return undefined\n\n const virtualSpineItem = reader.spineItemsManager.get(\n decodeURIComponentSafe(virtualSpineId),\n )\n\n if (\n !virtualSpineItem ||\n !parseVirtualPageSpreadFromHref(virtualSpineItem.item.href)\n ) {\n return undefined\n }\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: undefined,\n },\n spineId: virtualSpineItem.item.id,\n spineIndex: virtualSpineItem.item.index,\n })\n}\n\nexport const cbzEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & CbzEnhancerAPI => {\n const reader = next(options)\n\n const unregisterGenerateHook = reader.hookManager.register(\n \"cfi.afterGenerate\",\n ({ cfi, spineItem }) =>\n restoreOriginalSpineReference(reader, cfi, spineItem),\n )\n\n const unregisterResolveHook = reader.hookManager.register(\n \"cfi.beforeResolve\",\n ({ cfi }) => restoreVirtualSpineReference(reader, cfi),\n )\n\n const destroy = () => {\n unregisterGenerateHook()\n unregisterResolveHook()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_CBZ: true,\n destroy,\n }\n }\n","import { parseContentType } from \"@prose-reader/shared\"\nimport type { Archive } from \"@prose-reader/streamer\"\n\nexport const CBZ_MIME_TYPES: ReadonlySet<string> = new Set([\n \"application/vnd.comicbook+zip\",\n \"application/x-cbz\",\n])\n\nconst hasCbzExtension = (name: string) => name.toLowerCase().endsWith(\".cbz\")\n\nexport const isCbzArchive = (archive: Archive) => {\n const mimeType = parseContentType(archive.encodingFormat ?? \"\")\n\n if (mimeType && CBZ_MIME_TYPES.has(mimeType)) return true\n if (hasCbzExtension(archive.filename)) return true\n\n return false\n}\n","import type { StreamerManifestHookFactory } from \"@prose-reader/streamer\"\nimport { isCbzArchive } from \"./isCbzArchive\"\n\nexport const detectReadingDirectionManifest: StreamerManifestHookFactory =\n ({ archive }) =>\n (manifest) => {\n if (manifest.readingDirection !== undefined) return manifest\n if (!isCbzArchive(archive)) return manifest\n\n return {\n ...manifest,\n readingDirection: \"rtl\",\n }\n }\n","import type {\n StreamerManifestHookFactory,\n StreamerResourceHookFactory,\n} from \"@prose-reader/streamer\"\nimport { detectReadingDirectionManifest } from \"./detectReadingDirectionManifest\"\nimport { pageSpreadSplit } from \"./pageSpreadSplitManifest\"\nimport { pageSpreadSplitResourceHook } from \"./pageSpreadSplitResource\"\n\nexport const streamerHooks: {\n manifest: {\n content: StreamerManifestHookFactory[]\n spine: StreamerManifestHookFactory[]\n }\n resource: StreamerResourceHookFactory[]\n} = {\n manifest: {\n content: [detectReadingDirectionManifest],\n spine: [pageSpreadSplit],\n },\n resource: [pageSpreadSplitResourceHook],\n}\n\nexport { detectReadingDirectionManifest } from \"./detectReadingDirectionManifest\"\nexport { CBZ_MIME_TYPES, isCbzArchive } from \"./isCbzArchive\"\n\nexport {\n buildVirtualPageSpreadResourcePath,\n detectPageSpreadFromBasename,\n isPageSpreadSplitSupportedArchiveRecord,\n isPageSpreadSplitSupportedImage,\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n pageSpreadSplit,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\nexport {\n createPageSpreadSplitXhtml,\n pageSpreadSplitResourceHook,\n parseVirtualPageSpreadResourcePath,\n} from \"./pageSpreadSplitResource\"\n"],"names":["spreadPropertiesForSide","side","naturalSpreadSideAtIndex","index","readingDirection","isEvenSlot","openingPageSideForParityShift","isSpreadPair","firstItem","secondItem","firstSide","getItemSpreadPosition","secondSide","isSpreadPairNaturallyAligned","firstItemIndex","alignSpineItemsForSpreadParity","spineItems","openingItem","MAX_DETECTED_PAGE_NUMBER","numberFromPageLabel","label","value","detectPageLabelsFromBasename","basenameWithoutExtension","explicitPageRangeMatch","detectPageSpreadFromBasename","basename","match","firstPageLabel","secondPageLabel","firstPageNumber","secondPageNumber","PAGE_SPREAD_RESOURCE_PREFIX","PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE","supportedImageMediaTypes","isPageSpreadSplitSupportedImage","mimeType","encodeOriginalUriSegment","uri","createManifestResourceHref","baseUrl","resourcePath","hrefBaseUrl","hasOpfExtension","path","isArchiveEpub","archive","file","buildVirtualPageSpreadResourcePath","cropSide","originalUri","cropSidesInReadingOrder","createVirtualSpineItem","originalSpineItem","progressionWeight","createXmlSafeId","createVirtualManifestItem","href","id","mediaType","getArchiveRecordForManifestItem","spineItem","hrefCandidates","decodeManifestHref","resourcePathCandidates","getResourcePathCandidates","item","normalizeBaseUrl","candidates","normalizedBaseUrl","mediaTypeFromArchiveRecord","record","parseContentType","detectMimeTypeFromName","mediaTypeFromArchiveRecordResourcePath","isPageSpreadSplitSupportedArchiveRecord","resourcePathMediaType","pageSpreadSplit","manifest","virtualManifestItems","archiveRecord","detected","firstCropSide","secondCropSide","splitProgressionWeight","firstSpineItem","secondSpineItem","alignedSpineItems","imageDimensionsCache","decodeOriginalUriSegment","encoded","parseVirtualPageSpreadResourcePath","prefixIndex","parts","encodedOriginalUri","cropFileName","cropRectForSide","imageHeight","imageWidth","leftWidth","rightWidth","getRelativeOriginalImageSrc","readImageDimensions","source","bitmap","createPageSpreadSplitXhtml","imageDimensions","crop","escapeXmlAttributeValue","generatePageSpreadSplitResource","virtualResource","archiveCache","pageSpreadSplitResourceHook","resource","pageSpreadResource","VIRTUAL_SPINE_ID_EXTENSION","decodeURIComponentSafe","decodeURISafe","parseVirtualPageSpreadFromHref","getOriginalSpineIndex","reader","seenVirtualOriginalUris","originalSpineIndex","restoreOriginalSpineReference","cfi","updateEpubCfiSpineItemref","restoreVirtualSpineReference","virtualSpineId","getEpubCfiSpineItemref","virtualSpineItem","cbzEnhancer","next","options","unregisterGenerateHook","unregisterResolveHook","CBZ_MIME_TYPES","hasCbzExtension","name","isCbzArchive","detectReadingDirectionManifest","streamerHooks"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- (function(s,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("@prose-reader/cfi"),require("@prose-reader/shared")):typeof define=="function"&&define.amd?define(["exports","@prose-reader/cfi","@prose-reader/shared"],c):(s=typeof globalThis<"u"?globalThis:s||self,c(s["prose-reader-cbz"]={},s.cfi,s.shared))})(this,(function(s,c,p){"use strict";const v=e=>{const t=Number.parseInt(e,10);if(Number.isFinite(t)&&!(t<0||t>2e3))return t},$=e=>{const t=/(?:^|[\s._(-]|\[)p\s*(\d{1,5})\s*[-_]\s*(?:p\s*)?(\d{1,5})(?=$|[^\d])/i.exec(e);return t||/(?:^|[\s._(]|\[)(0\d{1,4})\s*[-_]\s*(0\d{1,4})(?=$|[^\d])/i.exec(e)},R=e=>{const t=e.replace(/\.[^.]+$/,""),r=$(t);if(!r)return;const[,i,o]=r;if(i===void 0||o===void 0)return;const n=v(i),a=v(o);if(!(n===void 0||a===void 0)&&a===n+1)return{firstPageLabel:i,secondPageLabel:o}},g="__prose-reader__/page-spread",l="application/xhtml+xml",x=new Set(["image/jpg","image/jpeg","image/png","image/webp"]),h=e=>e===void 0?!1:x.has(e),F=e=>encodeURIComponent(e),N=({baseUrl:e="",resourcePath:t})=>{if(!e&&/^https?:\/\//.test(t))return encodeURI(t);const r=e?`${e}${e.endsWith("/")?"":"/"}`:"file://";return encodeURI(`${r}${t}`)},P=e=>e.toLowerCase().endsWith(".opf"),O=e=>e.records.some(t=>!t.dir&&(P(t.basename)||P(t.uri))),I=({cropSide:e,originalUri:t})=>`${g}/${F(t)}/${e}.xhtml`,D=e=>e==="left"?{pageSpreadLeft:!0,pageSpreadRight:void 0}:{pageSpreadLeft:void 0,pageSpreadRight:!0},L=e=>e==="rtl"?["right","left"]:["left","right"],E=({baseUrl:e,cropSide:t,label:r,originalSpineItem:i,originalUri:o,progressionWeight:n})=>{const a=I({cropSide:t,originalUri:o});return{...i,id:`${i.id}.${r}`,href:N({baseUrl:e,resourcePath:a}),mediaType:l,progressionWeight:n,renditionLayout:"pre-paginated",...D(t)}},_=({href:e,id:t,mediaType:r})=>({href:e,id:t,mediaType:r}),W=({archive:e,baseUrl:t,spineItem:r})=>{const i=[r.href,B(r.href)],o=new Set(i.flatMap(n=>H(n,t)));return e.records.find(n=>!n.dir&&o.has(n.uri))},B=e=>{try{return decodeURI(e)}catch{return e}},k=e=>e.endsWith("/")?e:`${e}/`,H=(e,t)=>{const r=[e];if(e.startsWith("file://")&&r.push(e.slice(7)),t){const i=k(t);e.startsWith(i)&&r.push(e.slice(i.length))}return r},V=e=>p.parseContentType(e?.encodingFormat??"")||p.detectMimeTypeFromName(e?.basename??""),X=e=>p.detectMimeTypeFromName(e.uri)||p.detectMimeTypeFromName(e.basename),w=e=>{if(e===void 0||e.dir)return!1;const t=X(e);return h(t)?h(V(e)):!1},y=({archive:e,baseUrl:t})=>async r=>{if(O(e))return r;const i=[],o=r.spineItems.flatMap(n=>{const a=W({archive:e,baseUrl:t,spineItem:n});if(!w(a))return[n];const d=R(a.basename);if(d===void 0)return[n];const[re,ie]=L(r.readingDirection),T=n.progressionWeight!==void 0?n.progressionWeight/2:void 0,C=E({baseUrl:t,cropSide:re,label:d.firstPageLabel,originalSpineItem:n,originalUri:a.uri,progressionWeight:T}),A=E({baseUrl:t,cropSide:ie,label:d.secondPageLabel,originalSpineItem:n,originalUri:a.uri,progressionWeight:T});return i.push(_(C),_(A)),[C,A]});return i.length===0?r:{...r,spineItems:o.map((n,a)=>({...n,index:a})),items:[...r.items,...i]}},f=new WeakMap,G=e=>{try{return decodeURIComponent(e)}catch{return}},u=e=>{const t=e.indexOf(`${g}/`);if(t<0)return;const i=e.slice(t).split("/"),o=i[2],n=i[3];if(i.length!==4||i[0]!=="__prose-reader__"||i[1]!=="page-spread"||o===void 0||n===void 0)return;const a=n.split(".")[0];if(a!=="left"&&a!=="right")return;const d=G(o);if(d!==void 0)return{originalUri:d,cropSide:a}},z=({cropSide:e,imageHeight:t,imageWidth:r})=>{const i=Math.floor(r/2),o=r-i;return e==="left"?{x:0,width:i,height:t}:{x:i,width:o,height:t}},j=e=>/^https?:\/\//.test(e)?e:`../../../${encodeURI(e)}`,q=async e=>{if(typeof createImageBitmap!="function")throw new Error("Page spread XHTML generation requires createImageBitmap");const t=await createImageBitmap(e);try{return{height:t.height,width:t.width}}finally{t.close()}},b=({cropSide:e,imageDimensions:t,originalUri:r})=>{if(t.width<2)throw new Error("Page spread image is too narrow to split");const i=z({cropSide:e,imageHeight:t.height,imageWidth:t.width});return`<?xml version="1.0" encoding="UTF-8"?>
1
+ (function(s,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("@prose-reader/cfi"),require("@prose-reader/streamer"),require("@prose-reader/shared")):typeof define=="function"&&define.amd?define(["exports","@prose-reader/cfi","@prose-reader/streamer","@prose-reader/shared"],p):(s=typeof globalThis<"u"?globalThis:s||self,p(s["prose-reader-cbz"]={},s.cfi,s.streamer,s.shared))})(this,(function(s,p,P,c){"use strict";const O=e=>e==="left"?{pageSpreadLeft:!0,pageSpreadRight:void 0}:{pageSpreadLeft:void 0,pageSpreadRight:!0},R=({index:e,readingDirection:t})=>{const r=e%2===0;return t==="rtl"?r?"right":"left":r?"left":"right"},B=e=>e==="rtl"?"left":"right",W=({firstItem:e,secondItem:t})=>{if(e===void 0||t===void 0)return!1;const r=c.getItemSpreadPosition(e),i=c.getItemSpreadPosition(t);return r!==void 0&&i!==void 0&&r!==i},k=({firstItem:e,firstItemIndex:t,readingDirection:r,secondItem:i})=>c.getItemSpreadPosition(e)===R({index:t,readingDirection:r})&&c.getItemSpreadPosition(i)===R({index:t+1,readingDirection:r}),z=({readingDirection:e,spineItems:t})=>{const r=t[0];return r===void 0||c.getItemSpreadPosition(r)!==void 0||t.findIndex((n,o)=>{const a=t[o+1];return!W({firstItem:n,secondItem:a})||a===void 0?!1:!k({firstItem:n,firstItemIndex:o,readingDirection:e,secondItem:a})})<=0?t:[{...r,...O(B(e))},...t.slice(1)]},I=e=>{const t=Number.parseInt(e,10);if(Number.isFinite(t)&&!(t<0||t>2e3))return t},X=e=>{const t=/(?:^|[\s._(-]|\[)p\s*(\d{1,5})\s*[-_~]\s*(?:p\s*)?(\d{1,5})(?=$|[^\d])/i.exec(e);return t||/(?:^|[\s._(]|\[)(0\d{1,4})\s*[-_~]\s*(0\d{1,4})(?=$|[^\d])/i.exec(e)},E=e=>{const t=e.replace(/\.[^.]+$/,""),r=X(t);if(!r)return;const[,i,n]=r;if(i===void 0||n===void 0)return;const o=I(i),a=I(n);if(!(o===void 0||a===void 0)&&a===o+1)return{firstPageLabel:i,secondPageLabel:n}},g="__prose-reader__/page-spread",l="application/xhtml+xml",H=new Set(["image/jpg","image/jpeg","image/png","image/webp"]),f=e=>e===void 0?!1:H.has(e),V=e=>encodeURIComponent(e),G=({baseUrl:e="",resourcePath:t})=>{if(!e&&/^https?:\/\//.test(t))return encodeURI(t);const r=e?`${e}${e.endsWith("/")?"":"/"}`:"file://";return encodeURI(`${r}${t}`)},_=e=>e.toLowerCase().endsWith(".opf"),j=e=>e.records.some(t=>!t.dir&&(_(t.basename)||_(t.uri))),y=({cropSide:e,originalUri:t})=>`${g}/${V(t)}/${e}.xhtml`,q=e=>e==="left"?{pageSpreadLeft:!0,pageSpreadRight:void 0}:{pageSpreadLeft:void 0,pageSpreadRight:!0},Y=e=>e==="rtl"?["right","left"]:["left","right"],w=({baseUrl:e,cropSide:t,label:r,originalSpineItem:i,originalUri:n,progressionWeight:o})=>{const a=y({cropSide:t,originalUri:n});return{...i,id:P.createXmlSafeId(`${i.id}.${r}`),href:G({baseUrl:e,resourcePath:a}),mediaType:l,progressionWeight:o,renditionLayout:"pre-paginated",...q(t)}},b=({href:e,id:t,mediaType:r})=>({href:e,id:t,mediaType:r}),Z=({archive:e,baseUrl:t,spineItem:r})=>{const i=[r.href,J(r.href)],n=new Set(i.flatMap(o=>Q(o,t)));return e.records.find(o=>!o.dir&&n.has(o.uri))},J=e=>{try{return decodeURI(e)}catch{return e}},K=e=>e.endsWith("/")?e:`${e}/`,Q=(e,t)=>{const r=[e];if(e.startsWith("file://")&&r.push(e.slice(7)),t){const i=K(t);e.startsWith(i)&&r.push(e.slice(i.length))}return r},ee=e=>c.parseContentType(e?.encodingFormat??"")||c.detectMimeTypeFromName(e?.basename??""),te=e=>c.detectMimeTypeFromName(e.uri)||c.detectMimeTypeFromName(e.basename),M=e=>{if(e===void 0||e.dir)return!1;const t=te(e);return f(t)?f(ee(e)):!1},C=({archive:e,baseUrl:t})=>async r=>{if(j(e))return r;const i=[],n=r.spineItems.flatMap(a=>{const d=Z({archive:e,baseUrl:t,spineItem:a});if(!M(d))return[a];const v=E(d.basename);if(v===void 0)return[a];const[fe,he]=Y(r.readingDirection),D=a.progressionWeight!==void 0?a.progressionWeight/2:void 0,N=w({baseUrl:t,cropSide:fe,label:v.firstPageLabel,originalSpineItem:a,originalUri:d.uri,progressionWeight:D}),L=w({baseUrl:t,cropSide:he,label:v.secondPageLabel,originalSpineItem:a,originalUri:d.uri,progressionWeight:D});return i.push(b(N),b(L)),[N,L]}),o=z({readingDirection:r.readingDirection,spineItems:n});return i.length===0&&o===n?r:{...r,spineItems:o.map((a,d)=>({...a,index:d})),items:[...r.items,...i]}},h=new WeakMap,re=e=>{try{return decodeURIComponent(e)}catch{return}},u=e=>{const t=e.indexOf(`${g}/`);if(t<0)return;const i=e.slice(t).split("/"),n=i[2],o=i[3];if(i.length!==4||i[0]!=="__prose-reader__"||i[1]!=="page-spread"||n===void 0||o===void 0)return;const a=o.split(".")[0];if(a!=="left"&&a!=="right")return;const d=re(n);if(d!==void 0)return{originalUri:d,cropSide:a}},ie=({cropSide:e,imageHeight:t,imageWidth:r})=>{const i=Math.floor(r/2),n=r-i;return e==="left"?{x:0,width:i,height:t}:{x:i,width:n,height:t}},ne=e=>/^https?:\/\//.test(e)?e:`../../../${encodeURI(e)}`,oe=async e=>{if(typeof createImageBitmap!="function")throw new Error("Page spread XHTML generation requires createImageBitmap");const t=await createImageBitmap(e);try{return{height:t.height,width:t.width}}finally{t.close()}},U=({cropSide:e,imageDimensions:t,originalUri:r})=>{if(t.width<2)throw new Error("Page spread image is too narrow to split");const i=ie({cropSide:e,imageHeight:t.height,imageWidth:t.width});return`<?xml version="1.0" encoding="UTF-8"?>
2
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
3
  <head>
4
4
  <meta name="viewport" content="width=${i.width}, height=${i.height}" />
@@ -23,7 +23,7 @@
23
23
  </style>
24
24
  </head>
25
25
  <body>
26
- <img src="${p.escapeXmlAttributeValue(j(r))}" alt="" />
26
+ <img src="${c.escapeXmlAttributeValue(ne(r))}" alt="" />
27
27
  </body>
28
- </html>`},Y=async({archive:e,resourcePath:t})=>{const r=u(t);if(r===void 0)return;const i=e.records.find(d=>d.uri===r.originalUri&&!d.dir);if(i===void 0||i.dir)throw new Error(`no source file found for virtual page spread resourcePath:${t}`);const o=f.get(e)??new Map;f.has(e)||f.set(e,o);const n=o.get(r.originalUri)??await q(await i.blob());return o.set(r.originalUri,n),{body:b({cropSide:r.cropSide,imageDimensions:n,originalUri:r.originalUri}),params:{contentType:l}}},U=({archive:e,resourcePath:t})=>async r=>{const i=await Y({archive:e,resourcePath:t});return i===void 0?r:{...r,...i,params:{...r.params,...i.params}}},m="vnd.prose-reader.cbz.virtual-spine-id",M=e=>{try{return decodeURIComponent(e)}catch{return e}},Z=e=>{try{return decodeURI(e)}catch{return e}},S=e=>{const t=u(e)??u(Z(e));if(t)return{...t,originalUri:M(t.originalUri)}},J=(e,t)=>{const r=new Set;let i=0;for(const o of e.spineItemsManager.items){const n=S(o.item.href);if(!n){i++;continue}if(n.originalUri===t)return i;r.has(n.originalUri)||(r.add(n.originalUri),i++)}},K=(e,t,r)=>{const i=S(r.href);if(!i)return;const o=J(e,i.originalUri);if(o!==void 0)return c.updateEpubCfiSpineItemref(t,{extensions:{[m]:encodeURIComponent(r.id)},spineId:i.originalUri,spineIndex:o})},Q=(e,t)=>{const r=c.getEpubCfiSpineItemref(t)?.extensions?.[m];if(!r)return;const i=e.spineItemsManager.get(M(r));if(!(!i||!S(i.item.href)))return c.updateEpubCfiSpineItemref(t,{extensions:{[m]:void 0},spineId:i.item.id,spineIndex:i.item.index})},ee=e=>t=>{const r=e(t),i=r.hookManager.register("cfi.afterGenerate",({cfi:a,spineItem:d})=>K(r,a,d)),o=r.hookManager.register("cfi.beforeResolve",({cfi:a})=>Q(r,a));return{...r,__PROSE_READER_ENHANCER_CBZ:!0,destroy:()=>{i(),o(),r.destroy()}}},te={manifest:{spine:[y]},resource:[U]};s.PAGE_SPREAD_RESOURCE_PREFIX=g,s.PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE=l,s.buildVirtualPageSpreadResourcePath=I,s.cbzEnhancer=ee,s.createPageSpreadSplitXhtml=b,s.detectPageSpreadFromBasename=R,s.isPageSpreadSplitSupportedArchiveRecord=w,s.isPageSpreadSplitSupportedImage=h,s.pageSpreadSplit=y,s.pageSpreadSplitResourceHook=U,s.parseVirtualPageSpreadResourcePath=u,s.streamerHooks=te,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
28
+ </html>`},ae=async({archive:e,resourcePath:t})=>{const r=u(t);if(r===void 0)return;const i=e.records.find(d=>d.uri===r.originalUri&&!d.dir);if(i===void 0||i.dir)throw new Error(`no source file found for virtual page spread resourcePath:${t}`);const n=h.get(e)??new Map;h.has(e)||h.set(e,n);const o=n.get(r.originalUri)??await oe(await i.blob());return n.set(r.originalUri,o),{body:U({cropSide:r.cropSide,imageDimensions:o,originalUri:r.originalUri}),params:{contentType:l}}},T=({archive:e,resourcePath:t})=>async r=>{const i=await ae({archive:e,resourcePath:t});return i===void 0?r:{...r,...i,params:{...r.params,...i.params}}},S="vnd.prose-reader.cbz.virtual-spine-id",A=e=>{try{return decodeURIComponent(e)}catch{return e}},se=e=>{try{return decodeURI(e)}catch{return e}},m=e=>{const t=u(e)??u(se(e));if(t)return{...t,originalUri:A(t.originalUri)}},de=(e,t)=>{const r=new Set;let i=0;for(const n of e.spineItemsManager.items){const o=m(n.item.href);if(!o){i++;continue}if(o.originalUri===t)return i;r.has(o.originalUri)||(r.add(o.originalUri),i++)}},ce=(e,t,r)=>{const i=m(r.href);if(!i)return;const n=de(e,i.originalUri);if(n!==void 0)return p.updateEpubCfiSpineItemref(t,{extensions:{[S]:encodeURIComponent(r.id)},spineId:P.createXmlSafeId(i.originalUri),spineIndex:n})},pe=(e,t)=>{const r=p.getEpubCfiSpineItemref(t)?.extensions?.[S];if(!r)return;const i=e.spineItemsManager.get(A(r));if(!(!i||!m(i.item.href)))return p.updateEpubCfiSpineItemref(t,{extensions:{[S]:void 0},spineId:i.item.id,spineIndex:i.item.index})},ue=e=>t=>{const r=e(t),i=r.hookManager.register("cfi.afterGenerate",({cfi:a,spineItem:d})=>ce(r,a,d)),n=r.hookManager.register("cfi.beforeResolve",({cfi:a})=>pe(r,a));return{...r,__PROSE_READER_ENHANCER_CBZ:!0,destroy:()=>{i(),n(),r.destroy()}}},x=new Set(["application/vnd.comicbook+zip","application/x-cbz"]),ge=e=>e.toLowerCase().endsWith(".cbz"),$=e=>{const t=c.parseContentType(e.encodingFormat??"");return!!(t&&x.has(t)||ge(e.filename))},F=({archive:e})=>t=>t.readingDirection!==void 0||!$(e)?t:{...t,readingDirection:"rtl"},le={manifest:{content:[F],spine:[C]},resource:[T]};s.CBZ_MIME_TYPES=x,s.PAGE_SPREAD_RESOURCE_PREFIX=g,s.PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE=l,s.buildVirtualPageSpreadResourcePath=y,s.cbzEnhancer=ue,s.createPageSpreadSplitXhtml=U,s.detectPageSpreadFromBasename=E,s.detectReadingDirectionManifest=F,s.isCbzArchive=$,s.isPageSpreadSplitSupportedArchiveRecord=M,s.isPageSpreadSplitSupportedImage=f,s.pageSpreadSplit=C,s.pageSpreadSplitResourceHook=T,s.parseVirtualPageSpreadResourcePath=u,s.streamerHooks=le,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
29
29
  //# sourceMappingURL=index.umd.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.umd.cjs","sources":["../src/detectPageSpreadFromBasename.ts","../src/pageSpreadSplitManifest.ts","../src/pageSpreadSplitResource.ts","../src/enhancer.ts","../src/streamer.ts"],"sourcesContent":["/**\n * This detector intentionally stays filename-only and conservative.\n *\n * Future improvements should consider archive sequence context before widening\n * detection. For example, a spread such as `p002-003.jpg` is safer to split\n * when neighboring resources make the sequence plausible, like `p001.jpg` and\n * `p004.jpg`.\n */\nexport type DetectedPageSpread = {\n firstPageLabel: string\n secondPageLabel: string\n}\n\nconst MAX_DETECTED_PAGE_NUMBER = 2000\n\nconst numberFromPageLabel = (label: string): number | undefined => {\n const value = Number.parseInt(label, 10)\n\n if (!Number.isFinite(value)) return undefined\n if (value < 0 || value > MAX_DETECTED_PAGE_NUMBER) return undefined\n\n return value\n}\n\nconst detectPageLabelsFromBasename = (basenameWithoutExtension: string) => {\n const explicitPageRangeMatch =\n /(?:^|[\\s._(-]|\\[)p\\s*(\\d{1,5})\\s*[-_]\\s*(?:p\\s*)?(\\d{1,5})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n\n if (explicitPageRangeMatch) return explicitPageRangeMatch\n\n return /(?:^|[\\s._(]|\\[)(0\\d{1,4})\\s*[-_]\\s*(0\\d{1,4})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n}\n\nexport const detectPageSpreadFromBasename = (\n basename: string,\n): DetectedPageSpread | undefined => {\n const basenameWithoutExtension = basename.replace(/\\.[^.]+$/, ``)\n const match = detectPageLabelsFromBasename(basenameWithoutExtension)\n\n if (!match) return undefined\n\n const [, firstPageLabel, secondPageLabel] = match\n\n if (firstPageLabel === undefined || secondPageLabel === undefined) {\n return undefined\n }\n\n const firstPageNumber = numberFromPageLabel(firstPageLabel)\n const secondPageNumber = numberFromPageLabel(secondPageLabel)\n\n if (firstPageNumber === undefined || secondPageNumber === undefined) {\n return undefined\n }\n\n if (secondPageNumber !== firstPageNumber + 1) {\n return undefined\n }\n\n return {\n firstPageLabel,\n secondPageLabel,\n }\n}\n","import {\n detectMimeTypeFromName,\n type Manifest,\n parseContentType,\n} from \"@prose-reader/shared\"\nimport type { Archive } from \"@prose-reader/streamer\"\nimport { detectPageSpreadFromBasename } from \"./detectPageSpreadFromBasename\"\n\nexport {\n type DetectedPageSpread,\n detectPageSpreadFromBasename,\n} from \"./detectPageSpreadFromBasename\"\n\nexport type PageSpreadCropSide = \"left\" | \"right\"\n\nexport type VirtualPageSpreadResource = {\n originalUri: string\n cropSide: PageSpreadCropSide\n}\n\nexport const PAGE_SPREAD_RESOURCE_PREFIX = `__prose-reader__/page-spread`\nexport const PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE = `application/xhtml+xml`\n\nconst supportedImageMediaTypes = new Set([\n `image/jpg`,\n `image/jpeg`,\n `image/png`,\n `image/webp`,\n])\n\nexport const isPageSpreadSplitSupportedImage = (\n mimeType: string | undefined,\n) => {\n if (mimeType === undefined) return false\n\n return supportedImageMediaTypes.has(mimeType)\n}\n\ntype SpineItem = Manifest[\"spineItems\"][number]\ntype ManifestItem = Manifest[\"items\"][number]\ntype ArchiveRecord = Archive[\"records\"][number]\ntype ArchiveFileRecord = Extract<ArchiveRecord, { dir: false }>\n\nconst encodeOriginalUriSegment = (uri: string) => encodeURIComponent(uri)\n\nconst createManifestResourceHref = ({\n baseUrl = ``,\n resourcePath,\n}: {\n baseUrl?: string\n resourcePath: string\n}) => {\n if (!baseUrl && /^https?:\\/\\//.test(resourcePath)) {\n return encodeURI(resourcePath)\n }\n\n const hrefBaseUrl = baseUrl\n ? `${baseUrl}${baseUrl.endsWith(`/`) ? `` : `/`}`\n : `file://`\n\n return encodeURI(`${hrefBaseUrl}${resourcePath}`)\n}\n\nconst hasOpfExtension = (path: string) => path.toLowerCase().endsWith(`.opf`)\n\nconst isArchiveEpub = (archive: Archive) =>\n archive.records.some(\n (file) =>\n !file.dir &&\n (hasOpfExtension(file.basename) || hasOpfExtension(file.uri)),\n )\n\nexport const buildVirtualPageSpreadResourcePath = ({\n cropSide,\n originalUri,\n}: {\n originalUri: string\n cropSide: PageSpreadCropSide\n}) => {\n return `${PAGE_SPREAD_RESOURCE_PREFIX}/${encodeOriginalUriSegment(originalUri)}/${cropSide}.xhtml`\n}\n\nconst spreadPropertiesForSide = (\n side: PageSpreadCropSide,\n): Pick<SpineItem, \"pageSpreadLeft\" | \"pageSpreadRight\"> =>\n side === `left`\n ? { pageSpreadLeft: true, pageSpreadRight: undefined }\n : { pageSpreadLeft: undefined, pageSpreadRight: true }\n\nconst cropSidesInReadingOrder = (\n readingDirection: Manifest[\"readingDirection\"],\n): [PageSpreadCropSide, PageSpreadCropSide] =>\n readingDirection === `rtl` ? [`right`, `left`] : [`left`, `right`]\n\nconst createVirtualSpineItem = ({\n baseUrl,\n cropSide,\n label,\n originalSpineItem,\n originalUri,\n progressionWeight,\n}: {\n baseUrl: string\n originalSpineItem: SpineItem\n originalUri: string\n label: string\n cropSide: PageSpreadCropSide\n progressionWeight: number | undefined\n}): SpineItem => {\n const resourcePath = buildVirtualPageSpreadResourcePath({\n cropSide,\n originalUri,\n })\n\n return {\n ...originalSpineItem,\n id: `${originalSpineItem.id}.${label}`,\n href: createManifestResourceHref({ baseUrl, resourcePath }),\n mediaType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n progressionWeight,\n renditionLayout: `pre-paginated`,\n ...spreadPropertiesForSide(cropSide),\n }\n}\n\nconst createVirtualManifestItem = ({\n href,\n id,\n mediaType,\n}: Pick<ManifestItem, \"href\" | \"id\" | \"mediaType\">): ManifestItem => ({\n href,\n id,\n mediaType,\n})\n\nexport const getArchiveRecordForManifestItem = ({\n archive,\n baseUrl,\n spineItem,\n}: {\n archive: Archive\n baseUrl: string\n spineItem: Manifest[\"spineItems\"][number]\n}): ArchiveRecord | undefined => {\n const hrefCandidates = [spineItem.href, decodeManifestHref(spineItem.href)]\n const resourcePathCandidates = new Set(\n hrefCandidates.flatMap((href) => getResourcePathCandidates(href, baseUrl)),\n )\n\n return archive.records.find(\n (item) => !item.dir && resourcePathCandidates.has(item.uri),\n )\n}\n\nconst decodeManifestHref = (href: string) => {\n try {\n return decodeURI(href)\n } catch {\n return href\n }\n}\n\nconst normalizeBaseUrl = (baseUrl: string) =>\n baseUrl.endsWith(`/`) ? baseUrl : `${baseUrl}/`\n\nconst getResourcePathCandidates = (href: string, baseUrl: string) => {\n const candidates = [href]\n\n if (href.startsWith(`file://`)) {\n candidates.push(href.slice(`file://`.length))\n }\n\n if (baseUrl) {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl)\n\n if (href.startsWith(normalizedBaseUrl)) {\n candidates.push(href.slice(normalizedBaseUrl.length))\n }\n }\n\n return candidates\n}\n\nexport const mediaTypeFromArchiveRecord = (\n record:\n | {\n basename: string\n encodingFormat?: string\n }\n | undefined,\n) =>\n parseContentType(record?.encodingFormat ?? ``) ||\n detectMimeTypeFromName(record?.basename ?? ``)\n\nconst mediaTypeFromArchiveRecordResourcePath = (\n record: Pick<ArchiveRecord, \"basename\" | \"uri\">,\n) =>\n detectMimeTypeFromName(record.uri) || detectMimeTypeFromName(record.basename)\n\nexport const isPageSpreadSplitSupportedArchiveRecord = (\n record: ArchiveRecord | undefined,\n): record is ArchiveFileRecord => {\n if (record === undefined || record.dir) return false\n\n const resourcePathMediaType = mediaTypeFromArchiveRecordResourcePath(record)\n\n if (!isPageSpreadSplitSupportedImage(resourcePathMediaType)) return false\n\n return isPageSpreadSplitSupportedImage(mediaTypeFromArchiveRecord(record))\n}\n\nexport const pageSpreadSplit =\n ({ archive, baseUrl }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n if (isArchiveEpub(archive)) return manifest\n\n const virtualManifestItems: ManifestItem[] = []\n const spineItems = manifest.spineItems.flatMap((spineItem) => {\n const archiveRecord = getArchiveRecordForManifestItem({\n archive,\n baseUrl,\n spineItem,\n })\n\n if (!isPageSpreadSplitSupportedArchiveRecord(archiveRecord)) {\n return [spineItem]\n }\n\n const detected = detectPageSpreadFromBasename(archiveRecord.basename)\n\n if (detected === undefined) return [spineItem]\n\n const [firstCropSide, secondCropSide] = cropSidesInReadingOrder(\n manifest.readingDirection,\n )\n const splitProgressionWeight =\n spineItem.progressionWeight !== undefined\n ? spineItem.progressionWeight / 2\n : undefined\n const firstSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: firstCropSide,\n label: detected.firstPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n const secondSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: secondCropSide,\n label: detected.secondPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n\n virtualManifestItems.push(\n createVirtualManifestItem(firstSpineItem),\n createVirtualManifestItem(secondSpineItem),\n )\n\n return [firstSpineItem, secondSpineItem]\n })\n\n if (virtualManifestItems.length === 0) return manifest\n\n return {\n ...manifest,\n spineItems: spineItems.map((spineItem, index) => ({\n ...spineItem,\n index,\n })),\n items: [...manifest.items, ...virtualManifestItems],\n }\n }\n","import { escapeXmlAttributeValue } from \"@prose-reader/shared\"\nimport type { Archive, HookResource } from \"@prose-reader/streamer\"\nimport {\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\ntype CropRect = {\n x: number\n width: number\n height: number\n}\n\ntype ImageDimensions = {\n width: number\n height: number\n}\n\nconst imageDimensionsCache = new WeakMap<\n Archive,\n Map<string, ImageDimensions>\n>()\n\nconst decodeOriginalUriSegment = (encoded: string): string | undefined => {\n try {\n return decodeURIComponent(encoded)\n } catch {\n return undefined\n }\n}\n\nexport const parseVirtualPageSpreadResourcePath = (\n resourcePath: string,\n): VirtualPageSpreadResource | undefined => {\n const prefixIndex = resourcePath.indexOf(`${PAGE_SPREAD_RESOURCE_PREFIX}/`)\n\n if (prefixIndex < 0) return undefined\n\n const virtualPath = resourcePath.slice(prefixIndex)\n const parts = virtualPath.split(`/`)\n\n const encodedOriginalUri = parts[2]\n const cropFileName = parts[3]\n\n if (\n parts.length !== 4 ||\n parts[0] !== `__prose-reader__` ||\n parts[1] !== `page-spread` ||\n encodedOriginalUri === undefined ||\n cropFileName === undefined\n ) {\n return undefined\n }\n\n const cropSide = cropFileName.split(`.`)[0]\n\n if (cropSide !== `left` && cropSide !== `right`) return undefined\n\n const originalUri = decodeOriginalUriSegment(encodedOriginalUri)\n\n if (originalUri === undefined) return undefined\n\n return {\n originalUri,\n cropSide,\n }\n}\n\nconst cropRectForSide = ({\n cropSide,\n imageHeight,\n imageWidth,\n}: {\n cropSide: PageSpreadCropSide\n imageWidth: number\n imageHeight: number\n}): CropRect => {\n const leftWidth = Math.floor(imageWidth / 2)\n const rightWidth = imageWidth - leftWidth\n\n return cropSide === `left`\n ? { x: 0, width: leftWidth, height: imageHeight }\n : { x: leftWidth, width: rightWidth, height: imageHeight }\n}\n\n/**\n * Since we create a virtual sub path we need to use the relative path to the original image.\n * There is no \"real\" path but the streamer does not need to know that.\n */\nconst getRelativeOriginalImageSrc = (originalUri: string) => {\n if (/^https?:\\/\\//.test(originalUri)) return originalUri\n\n return `../../../${encodeURI(originalUri)}`\n}\n\nconst readImageDimensions = async (source: Blob): Promise<ImageDimensions> => {\n if (typeof createImageBitmap !== `function`) {\n throw new Error(`Page spread XHTML generation requires createImageBitmap`)\n }\n\n const bitmap = await createImageBitmap(source)\n\n try {\n return {\n height: bitmap.height,\n width: bitmap.width,\n }\n } finally {\n bitmap.close()\n }\n}\n\nexport const createPageSpreadSplitXhtml = ({\n cropSide,\n imageDimensions,\n originalUri,\n}: {\n cropSide: PageSpreadCropSide\n imageDimensions: ImageDimensions\n originalUri: string\n}): string => {\n if (imageDimensions.width < 2) {\n throw new Error(`Page spread image is too narrow to split`)\n }\n\n const crop = cropRectForSide({\n cropSide,\n imageHeight: imageDimensions.height,\n imageWidth: imageDimensions.width,\n })\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <meta name=\"viewport\" content=\"width=${crop.width}, height=${crop.height}\" />\n <style>\n html,\n body {\n width: ${crop.width}px;\n height: ${crop.height}px;\n margin: 0;\n overflow: hidden;\n }\n\n img {\n display: block;\n width: ${imageDimensions.width}px;\n height: ${imageDimensions.height}px;\n max-width: none;\n transform: translateX(-${crop.x}px);\n user-select: none;\n -webkit-user-drag: none;\n }\n </style>\n </head>\n <body>\n <img src=\"${escapeXmlAttributeValue(getRelativeOriginalImageSrc(originalUri))}\" alt=\"\" />\n </body>\n</html>`\n}\n\nconst generatePageSpreadSplitResource = async ({\n archive,\n resourcePath,\n}: {\n archive: Archive\n resourcePath: string\n}): Promise<HookResource | undefined> => {\n const virtualResource = parseVirtualPageSpreadResourcePath(resourcePath)\n\n if (virtualResource === undefined) return undefined\n\n const file = archive.records.find(\n (file) => file.uri === virtualResource.originalUri && !file.dir,\n )\n\n if (file === undefined || file.dir) {\n throw new Error(\n `no source file found for virtual page spread resourcePath:${resourcePath}`,\n )\n }\n\n const archiveCache = imageDimensionsCache.get(archive) ?? new Map()\n\n if (!imageDimensionsCache.has(archive)) {\n imageDimensionsCache.set(archive, archiveCache)\n }\n\n const imageDimensions =\n archiveCache.get(virtualResource.originalUri) ??\n (await readImageDimensions(await file.blob()))\n\n archiveCache.set(virtualResource.originalUri, imageDimensions)\n\n const body = createPageSpreadSplitXhtml({\n cropSide: virtualResource.cropSide,\n imageDimensions,\n originalUri: virtualResource.originalUri,\n })\n\n return {\n body,\n params: {\n contentType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n },\n }\n}\n\nexport const pageSpreadSplitResourceHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const pageSpreadResource = await generatePageSpreadSplitResource({\n archive,\n resourcePath,\n })\n\n if (pageSpreadResource === undefined) return resource\n\n return {\n ...resource,\n ...pageSpreadResource,\n params: {\n ...resource.params,\n ...pageSpreadResource.params,\n },\n }\n }\n","import {\n getEpubCfiSpineItemref,\n updateEpubCfiSpineItemref,\n} from \"@prose-reader/cfi\"\nimport type { Reader } from \"@prose-reader/core\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { parseVirtualPageSpreadResourcePath } from \"./pageSpreadSplitResource\"\n\nconst VIRTUAL_SPINE_ID_EXTENSION = \"vnd.prose-reader.cbz.virtual-spine-id\"\n\ntype SpineItem = Manifest[\"spineItems\"][number]\n\nexport type CbzEnhancerAPI = {\n __PROSE_READER_ENHANCER_CBZ: true\n}\n\nconst decodeURIComponentSafe = (value: string) => {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\nconst decodeURISafe = (value: string) => {\n try {\n return decodeURI(value)\n } catch {\n return value\n }\n}\n\nconst parseVirtualPageSpreadFromHref = (href: string) => {\n const virtualResource =\n parseVirtualPageSpreadResourcePath(href) ??\n parseVirtualPageSpreadResourcePath(decodeURISafe(href))\n\n if (!virtualResource) return undefined\n\n return {\n ...virtualResource,\n originalUri: decodeURIComponentSafe(virtualResource.originalUri),\n }\n}\n\nconst getOriginalSpineIndex = (reader: Reader, originalUri: string) => {\n const seenVirtualOriginalUris = new Set<string>()\n let originalSpineIndex = 0\n\n for (const spineItem of reader.spineItemsManager.items) {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.item.href)\n\n if (!virtualResource) {\n originalSpineIndex++\n continue\n }\n\n if (virtualResource.originalUri === originalUri) {\n return originalSpineIndex\n }\n\n if (!seenVirtualOriginalUris.has(virtualResource.originalUri)) {\n seenVirtualOriginalUris.add(virtualResource.originalUri)\n originalSpineIndex++\n }\n }\n\n return undefined\n}\n\nconst restoreOriginalSpineReference = (\n reader: Reader,\n cfi: string,\n spineItem: SpineItem,\n) => {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.href)\n\n if (!virtualResource) return undefined\n\n const originalSpineIndex = getOriginalSpineIndex(\n reader,\n virtualResource.originalUri,\n )\n\n if (originalSpineIndex === undefined) return undefined\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: encodeURIComponent(spineItem.id),\n },\n spineId: virtualResource.originalUri,\n spineIndex: originalSpineIndex,\n })\n}\n\nconst restoreVirtualSpineReference = (reader: Reader, cfi: string) => {\n const virtualSpineId =\n getEpubCfiSpineItemref(cfi)?.extensions?.[VIRTUAL_SPINE_ID_EXTENSION]\n\n if (!virtualSpineId) return undefined\n\n const virtualSpineItem = reader.spineItemsManager.get(\n decodeURIComponentSafe(virtualSpineId),\n )\n\n if (\n !virtualSpineItem ||\n !parseVirtualPageSpreadFromHref(virtualSpineItem.item.href)\n ) {\n return undefined\n }\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: undefined,\n },\n spineId: virtualSpineItem.item.id,\n spineIndex: virtualSpineItem.item.index,\n })\n}\n\nexport const cbzEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & CbzEnhancerAPI => {\n const reader = next(options)\n\n const unregisterGenerateHook = reader.hookManager.register(\n \"cfi.afterGenerate\",\n ({ cfi, spineItem }) =>\n restoreOriginalSpineReference(reader, cfi, spineItem),\n )\n\n const unregisterResolveHook = reader.hookManager.register(\n \"cfi.beforeResolve\",\n ({ cfi }) => restoreVirtualSpineReference(reader, cfi),\n )\n\n const destroy = () => {\n unregisterGenerateHook()\n unregisterResolveHook()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_CBZ: true,\n destroy,\n }\n }\n","import type {\n StreamerManifestHookFactory,\n StreamerResourceHookFactory,\n} from \"@prose-reader/streamer\"\nimport { pageSpreadSplit } from \"./pageSpreadSplitManifest\"\nimport { pageSpreadSplitResourceHook } from \"./pageSpreadSplitResource\"\n\nexport const streamerHooks: {\n manifest: {\n spine: StreamerManifestHookFactory[]\n }\n resource: StreamerResourceHookFactory[]\n} = {\n manifest: {\n spine: [pageSpreadSplit],\n },\n resource: [pageSpreadSplitResourceHook],\n}\n\nexport {\n buildVirtualPageSpreadResourcePath,\n detectPageSpreadFromBasename,\n isPageSpreadSplitSupportedArchiveRecord,\n isPageSpreadSplitSupportedImage,\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n pageSpreadSplit,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\nexport {\n createPageSpreadSplitXhtml,\n pageSpreadSplitResourceHook,\n parseVirtualPageSpreadResourcePath,\n} from \"./pageSpreadSplitResource\"\n"],"names":["numberFromPageLabel","label","value","detectPageLabelsFromBasename","basenameWithoutExtension","explicitPageRangeMatch","detectPageSpreadFromBasename","basename","match","firstPageLabel","secondPageLabel","firstPageNumber","secondPageNumber","PAGE_SPREAD_RESOURCE_PREFIX","PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE","supportedImageMediaTypes","isPageSpreadSplitSupportedImage","mimeType","encodeOriginalUriSegment","uri","createManifestResourceHref","baseUrl","resourcePath","hrefBaseUrl","hasOpfExtension","path","isArchiveEpub","archive","file","buildVirtualPageSpreadResourcePath","cropSide","originalUri","spreadPropertiesForSide","side","cropSidesInReadingOrder","readingDirection","createVirtualSpineItem","originalSpineItem","progressionWeight","createVirtualManifestItem","href","id","mediaType","getArchiveRecordForManifestItem","spineItem","hrefCandidates","decodeManifestHref","resourcePathCandidates","getResourcePathCandidates","item","normalizeBaseUrl","candidates","normalizedBaseUrl","mediaTypeFromArchiveRecord","record","parseContentType","detectMimeTypeFromName","mediaTypeFromArchiveRecordResourcePath","isPageSpreadSplitSupportedArchiveRecord","resourcePathMediaType","pageSpreadSplit","manifest","virtualManifestItems","spineItems","archiveRecord","detected","firstCropSide","secondCropSide","splitProgressionWeight","firstSpineItem","secondSpineItem","index","imageDimensionsCache","decodeOriginalUriSegment","encoded","parseVirtualPageSpreadResourcePath","prefixIndex","parts","encodedOriginalUri","cropFileName","cropRectForSide","imageHeight","imageWidth","leftWidth","rightWidth","getRelativeOriginalImageSrc","readImageDimensions","source","bitmap","createPageSpreadSplitXhtml","imageDimensions","crop","escapeXmlAttributeValue","generatePageSpreadSplitResource","virtualResource","archiveCache","pageSpreadSplitResourceHook","resource","pageSpreadResource","VIRTUAL_SPINE_ID_EXTENSION","decodeURIComponentSafe","decodeURISafe","parseVirtualPageSpreadFromHref","getOriginalSpineIndex","reader","seenVirtualOriginalUris","originalSpineIndex","restoreOriginalSpineReference","cfi","updateEpubCfiSpineItemref","restoreVirtualSpineReference","virtualSpineId","getEpubCfiSpineItemref","virtualSpineItem","cbzEnhancer","next","options","unregisterGenerateHook","unregisterResolveHook","streamerHooks"],"mappings":"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"}
1
+ {"version":3,"file":"index.umd.cjs","sources":["../src/alignSpineItemsForSpreadParity.ts","../src/detectPageSpreadFromBasename.ts","../src/pageSpreadSplitManifest.ts","../src/pageSpreadSplitResource.ts","../src/enhancer.ts","../src/isCbzArchive.ts","../src/detectReadingDirectionManifest.ts","../src/streamer.ts"],"sourcesContent":["import { getItemSpreadPosition, type Manifest } from \"@prose-reader/shared\"\n\ntype SpineItem = Manifest[\"spineItems\"][number]\ntype PageSpreadSide = \"left\" | \"right\"\n\nconst spreadPropertiesForSide = (\n side: PageSpreadSide,\n): Pick<SpineItem, \"pageSpreadLeft\" | \"pageSpreadRight\"> =>\n side === `left`\n ? { pageSpreadLeft: true, pageSpreadRight: undefined }\n : { pageSpreadLeft: undefined, pageSpreadRight: true }\n\nconst naturalSpreadSideAtIndex = ({\n index,\n readingDirection,\n}: {\n index: number\n readingDirection: Manifest[\"readingDirection\"]\n}): PageSpreadSide => {\n const isEvenSlot = index % 2 === 0\n\n return readingDirection === `rtl`\n ? isEvenSlot\n ? `right`\n : `left`\n : isEvenSlot\n ? `left`\n : `right`\n}\n\nconst openingPageSideForParityShift = (\n readingDirection: Manifest[\"readingDirection\"],\n): PageSpreadSide => (readingDirection === `rtl` ? `left` : `right`)\n\nconst isSpreadPair = ({\n firstItem,\n secondItem,\n}: {\n firstItem: SpineItem | undefined\n secondItem: SpineItem | undefined\n}) => {\n if (firstItem === undefined || secondItem === undefined) return false\n\n const firstSide = getItemSpreadPosition(firstItem)\n const secondSide = getItemSpreadPosition(secondItem)\n\n return (\n firstSide !== undefined &&\n secondSide !== undefined &&\n firstSide !== secondSide\n )\n}\n\nconst isSpreadPairNaturallyAligned = ({\n firstItem,\n firstItemIndex,\n readingDirection,\n secondItem,\n}: {\n firstItem: SpineItem\n firstItemIndex: number\n readingDirection: Manifest[\"readingDirection\"]\n secondItem: SpineItem\n}) =>\n getItemSpreadPosition(firstItem) ===\n naturalSpreadSideAtIndex({\n index: firstItemIndex,\n readingDirection,\n }) &&\n getItemSpreadPosition(secondItem) ===\n naturalSpreadSideAtIndex({\n index: firstItemIndex + 1,\n readingDirection,\n })\n\nexport const alignSpineItemsForSpreadParity = ({\n readingDirection,\n spineItems,\n}: {\n readingDirection: Manifest[\"readingDirection\"]\n spineItems: SpineItem[]\n}) => {\n const openingItem = spineItems[0]\n\n if (\n openingItem === undefined ||\n getItemSpreadPosition(openingItem) !== undefined\n ) {\n return spineItems\n }\n\n const misalignedPairIndex = spineItems.findIndex((firstItem, index) => {\n const secondItem = spineItems[index + 1]\n\n if (\n !isSpreadPair({\n firstItem,\n secondItem,\n })\n ) {\n return false\n }\n\n if (secondItem === undefined) return false\n\n return !isSpreadPairNaturallyAligned({\n firstItem,\n firstItemIndex: index,\n readingDirection,\n secondItem,\n })\n })\n\n if (misalignedPairIndex <= 0) return spineItems\n\n return [\n {\n ...openingItem,\n ...spreadPropertiesForSide(\n openingPageSideForParityShift(readingDirection),\n ),\n },\n ...spineItems.slice(1),\n ]\n}\n","/**\n * This detector intentionally stays filename-only and conservative.\n *\n * Future improvements should consider archive sequence context before widening\n * detection. For example, a spread such as `p002-003.jpg` is safer to split\n * when neighboring resources make the sequence plausible, like `p001.jpg` and\n * `p004.jpg`.\n */\nexport type DetectedPageSpread = {\n firstPageLabel: string\n secondPageLabel: string\n}\n\nconst MAX_DETECTED_PAGE_NUMBER = 2000\n\nconst numberFromPageLabel = (label: string): number | undefined => {\n const value = Number.parseInt(label, 10)\n\n if (!Number.isFinite(value)) return undefined\n if (value < 0 || value > MAX_DETECTED_PAGE_NUMBER) return undefined\n\n return value\n}\n\nconst detectPageLabelsFromBasename = (basenameWithoutExtension: string) => {\n const explicitPageRangeMatch =\n /(?:^|[\\s._(-]|\\[)p\\s*(\\d{1,5})\\s*[-_~]\\s*(?:p\\s*)?(\\d{1,5})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n\n if (explicitPageRangeMatch) return explicitPageRangeMatch\n\n return /(?:^|[\\s._(]|\\[)(0\\d{1,4})\\s*[-_~]\\s*(0\\d{1,4})(?=$|[^\\d])/i.exec(\n basenameWithoutExtension,\n )\n}\n\nexport const detectPageSpreadFromBasename = (\n basename: string,\n): DetectedPageSpread | undefined => {\n const basenameWithoutExtension = basename.replace(/\\.[^.]+$/, ``)\n const match = detectPageLabelsFromBasename(basenameWithoutExtension)\n\n if (!match) return undefined\n\n const [, firstPageLabel, secondPageLabel] = match\n\n if (firstPageLabel === undefined || secondPageLabel === undefined) {\n return undefined\n }\n\n const firstPageNumber = numberFromPageLabel(firstPageLabel)\n const secondPageNumber = numberFromPageLabel(secondPageLabel)\n\n if (firstPageNumber === undefined || secondPageNumber === undefined) {\n return undefined\n }\n\n if (secondPageNumber !== firstPageNumber + 1) {\n return undefined\n }\n\n return {\n firstPageLabel,\n secondPageLabel,\n }\n}\n","import {\n detectMimeTypeFromName,\n type Manifest,\n parseContentType,\n} from \"@prose-reader/shared\"\nimport { type Archive, createXmlSafeId } from \"@prose-reader/streamer\"\nimport { alignSpineItemsForSpreadParity } from \"./alignSpineItemsForSpreadParity\"\nimport { detectPageSpreadFromBasename } from \"./detectPageSpreadFromBasename\"\n\nexport {\n type DetectedPageSpread,\n detectPageSpreadFromBasename,\n} from \"./detectPageSpreadFromBasename\"\n\nexport type PageSpreadCropSide = \"left\" | \"right\"\n\nexport type VirtualPageSpreadResource = {\n originalUri: string\n cropSide: PageSpreadCropSide\n}\n\nexport const PAGE_SPREAD_RESOURCE_PREFIX = `__prose-reader__/page-spread`\nexport const PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE = `application/xhtml+xml`\n\nconst supportedImageMediaTypes = new Set([\n `image/jpg`,\n `image/jpeg`,\n `image/png`,\n `image/webp`,\n])\n\nexport const isPageSpreadSplitSupportedImage = (\n mimeType: string | undefined,\n) => {\n if (mimeType === undefined) return false\n\n return supportedImageMediaTypes.has(mimeType)\n}\n\ntype SpineItem = Manifest[\"spineItems\"][number]\ntype ManifestItem = Manifest[\"items\"][number]\ntype ArchiveRecord = Archive[\"records\"][number]\ntype ArchiveFileRecord = Extract<ArchiveRecord, { dir: false }>\n\nconst encodeOriginalUriSegment = (uri: string) => encodeURIComponent(uri)\n\nconst createManifestResourceHref = ({\n baseUrl = ``,\n resourcePath,\n}: {\n baseUrl?: string\n resourcePath: string\n}) => {\n if (!baseUrl && /^https?:\\/\\//.test(resourcePath)) {\n return encodeURI(resourcePath)\n }\n\n const hrefBaseUrl = baseUrl\n ? `${baseUrl}${baseUrl.endsWith(`/`) ? `` : `/`}`\n : `file://`\n\n return encodeURI(`${hrefBaseUrl}${resourcePath}`)\n}\n\nconst hasOpfExtension = (path: string) => path.toLowerCase().endsWith(`.opf`)\n\nconst isArchiveEpub = (archive: Archive) =>\n archive.records.some(\n (file) =>\n !file.dir &&\n (hasOpfExtension(file.basename) || hasOpfExtension(file.uri)),\n )\n\nexport const buildVirtualPageSpreadResourcePath = ({\n cropSide,\n originalUri,\n}: {\n originalUri: string\n cropSide: PageSpreadCropSide\n}) => {\n return `${PAGE_SPREAD_RESOURCE_PREFIX}/${encodeOriginalUriSegment(originalUri)}/${cropSide}.xhtml`\n}\n\nconst spreadPropertiesForSide = (\n side: PageSpreadCropSide,\n): Pick<SpineItem, \"pageSpreadLeft\" | \"pageSpreadRight\"> =>\n side === `left`\n ? { pageSpreadLeft: true, pageSpreadRight: undefined }\n : { pageSpreadLeft: undefined, pageSpreadRight: true }\n\nconst cropSidesInReadingOrder = (\n readingDirection: Manifest[\"readingDirection\"],\n): [PageSpreadCropSide, PageSpreadCropSide] =>\n readingDirection === `rtl` ? [`right`, `left`] : [`left`, `right`]\n\nconst createVirtualSpineItem = ({\n baseUrl,\n cropSide,\n label,\n originalSpineItem,\n originalUri,\n progressionWeight,\n}: {\n baseUrl: string\n originalSpineItem: SpineItem\n originalUri: string\n label: string\n cropSide: PageSpreadCropSide\n progressionWeight: number | undefined\n}): SpineItem => {\n const resourcePath = buildVirtualPageSpreadResourcePath({\n cropSide,\n originalUri,\n })\n\n return {\n ...originalSpineItem,\n id: createXmlSafeId(`${originalSpineItem.id}.${label}`),\n href: createManifestResourceHref({ baseUrl, resourcePath }),\n mediaType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n progressionWeight,\n renditionLayout: `pre-paginated`,\n ...spreadPropertiesForSide(cropSide),\n }\n}\n\nconst createVirtualManifestItem = ({\n href,\n id,\n mediaType,\n}: Pick<ManifestItem, \"href\" | \"id\" | \"mediaType\">): ManifestItem => ({\n href,\n id,\n mediaType,\n})\n\nexport const getArchiveRecordForManifestItem = ({\n archive,\n baseUrl,\n spineItem,\n}: {\n archive: Archive\n baseUrl: string\n spineItem: Manifest[\"spineItems\"][number]\n}): ArchiveRecord | undefined => {\n const hrefCandidates = [spineItem.href, decodeManifestHref(spineItem.href)]\n const resourcePathCandidates = new Set(\n hrefCandidates.flatMap((href) => getResourcePathCandidates(href, baseUrl)),\n )\n\n return archive.records.find(\n (item) => !item.dir && resourcePathCandidates.has(item.uri),\n )\n}\n\nconst decodeManifestHref = (href: string) => {\n try {\n return decodeURI(href)\n } catch {\n return href\n }\n}\n\nconst normalizeBaseUrl = (baseUrl: string) =>\n baseUrl.endsWith(`/`) ? baseUrl : `${baseUrl}/`\n\nconst getResourcePathCandidates = (href: string, baseUrl: string) => {\n const candidates = [href]\n\n if (href.startsWith(`file://`)) {\n candidates.push(href.slice(`file://`.length))\n }\n\n if (baseUrl) {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl)\n\n if (href.startsWith(normalizedBaseUrl)) {\n candidates.push(href.slice(normalizedBaseUrl.length))\n }\n }\n\n return candidates\n}\n\nexport const mediaTypeFromArchiveRecord = (\n record:\n | {\n basename: string\n encodingFormat?: string\n }\n | undefined,\n) =>\n parseContentType(record?.encodingFormat ?? ``) ||\n detectMimeTypeFromName(record?.basename ?? ``)\n\nconst mediaTypeFromArchiveRecordResourcePath = (\n record: Pick<ArchiveRecord, \"basename\" | \"uri\">,\n) =>\n detectMimeTypeFromName(record.uri) || detectMimeTypeFromName(record.basename)\n\nexport const isPageSpreadSplitSupportedArchiveRecord = (\n record: ArchiveRecord | undefined,\n): record is ArchiveFileRecord => {\n if (record === undefined || record.dir) return false\n\n const resourcePathMediaType = mediaTypeFromArchiveRecordResourcePath(record)\n\n if (!isPageSpreadSplitSupportedImage(resourcePathMediaType)) return false\n\n return isPageSpreadSplitSupportedImage(mediaTypeFromArchiveRecord(record))\n}\n\nexport const pageSpreadSplit =\n ({ archive, baseUrl }: { archive: Archive; baseUrl: string }) =>\n async (manifest: Manifest): Promise<Manifest> => {\n if (isArchiveEpub(archive)) return manifest\n\n const virtualManifestItems: ManifestItem[] = []\n const spineItems = manifest.spineItems.flatMap((spineItem) => {\n const archiveRecord = getArchiveRecordForManifestItem({\n archive,\n baseUrl,\n spineItem,\n })\n\n if (!isPageSpreadSplitSupportedArchiveRecord(archiveRecord)) {\n return [spineItem]\n }\n\n const detected = detectPageSpreadFromBasename(archiveRecord.basename)\n\n if (detected === undefined) return [spineItem]\n\n const [firstCropSide, secondCropSide] = cropSidesInReadingOrder(\n manifest.readingDirection,\n )\n const splitProgressionWeight =\n spineItem.progressionWeight !== undefined\n ? spineItem.progressionWeight / 2\n : undefined\n const firstSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: firstCropSide,\n label: detected.firstPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n const secondSpineItem = createVirtualSpineItem({\n baseUrl,\n cropSide: secondCropSide,\n label: detected.secondPageLabel,\n originalSpineItem: spineItem,\n originalUri: archiveRecord.uri,\n progressionWeight: splitProgressionWeight,\n })\n\n virtualManifestItems.push(\n createVirtualManifestItem(firstSpineItem),\n createVirtualManifestItem(secondSpineItem),\n )\n\n return [firstSpineItem, secondSpineItem]\n })\n\n const alignedSpineItems = alignSpineItemsForSpreadParity({\n readingDirection: manifest.readingDirection,\n spineItems,\n })\n\n if (virtualManifestItems.length === 0 && alignedSpineItems === spineItems) {\n return manifest\n }\n\n return {\n ...manifest,\n spineItems: alignedSpineItems.map((spineItem, index) => ({\n ...spineItem,\n index,\n })),\n items: [...manifest.items, ...virtualManifestItems],\n }\n }\n","import { escapeXmlAttributeValue } from \"@prose-reader/shared\"\nimport type { Archive, HookResource } from \"@prose-reader/streamer\"\nimport {\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\ntype CropRect = {\n x: number\n width: number\n height: number\n}\n\ntype ImageDimensions = {\n width: number\n height: number\n}\n\nconst imageDimensionsCache = new WeakMap<\n Archive,\n Map<string, ImageDimensions>\n>()\n\nconst decodeOriginalUriSegment = (encoded: string): string | undefined => {\n try {\n return decodeURIComponent(encoded)\n } catch {\n return undefined\n }\n}\n\nexport const parseVirtualPageSpreadResourcePath = (\n resourcePath: string,\n): VirtualPageSpreadResource | undefined => {\n const prefixIndex = resourcePath.indexOf(`${PAGE_SPREAD_RESOURCE_PREFIX}/`)\n\n if (prefixIndex < 0) return undefined\n\n const virtualPath = resourcePath.slice(prefixIndex)\n const parts = virtualPath.split(`/`)\n\n const encodedOriginalUri = parts[2]\n const cropFileName = parts[3]\n\n if (\n parts.length !== 4 ||\n parts[0] !== `__prose-reader__` ||\n parts[1] !== `page-spread` ||\n encodedOriginalUri === undefined ||\n cropFileName === undefined\n ) {\n return undefined\n }\n\n const cropSide = cropFileName.split(`.`)[0]\n\n if (cropSide !== `left` && cropSide !== `right`) return undefined\n\n const originalUri = decodeOriginalUriSegment(encodedOriginalUri)\n\n if (originalUri === undefined) return undefined\n\n return {\n originalUri,\n cropSide,\n }\n}\n\nconst cropRectForSide = ({\n cropSide,\n imageHeight,\n imageWidth,\n}: {\n cropSide: PageSpreadCropSide\n imageWidth: number\n imageHeight: number\n}): CropRect => {\n const leftWidth = Math.floor(imageWidth / 2)\n const rightWidth = imageWidth - leftWidth\n\n return cropSide === `left`\n ? { x: 0, width: leftWidth, height: imageHeight }\n : { x: leftWidth, width: rightWidth, height: imageHeight }\n}\n\n/**\n * Since we create a virtual sub path we need to use the relative path to the original image.\n * There is no \"real\" path but the streamer does not need to know that.\n */\nconst getRelativeOriginalImageSrc = (originalUri: string) => {\n if (/^https?:\\/\\//.test(originalUri)) return originalUri\n\n return `../../../${encodeURI(originalUri)}`\n}\n\nconst readImageDimensions = async (source: Blob): Promise<ImageDimensions> => {\n if (typeof createImageBitmap !== `function`) {\n throw new Error(`Page spread XHTML generation requires createImageBitmap`)\n }\n\n const bitmap = await createImageBitmap(source)\n\n try {\n return {\n height: bitmap.height,\n width: bitmap.width,\n }\n } finally {\n bitmap.close()\n }\n}\n\nexport const createPageSpreadSplitXhtml = ({\n cropSide,\n imageDimensions,\n originalUri,\n}: {\n cropSide: PageSpreadCropSide\n imageDimensions: ImageDimensions\n originalUri: string\n}): string => {\n if (imageDimensions.width < 2) {\n throw new Error(`Page spread image is too narrow to split`)\n }\n\n const crop = cropRectForSide({\n cropSide,\n imageHeight: imageDimensions.height,\n imageWidth: imageDimensions.width,\n })\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n <head>\n <meta name=\"viewport\" content=\"width=${crop.width}, height=${crop.height}\" />\n <style>\n html,\n body {\n width: ${crop.width}px;\n height: ${crop.height}px;\n margin: 0;\n overflow: hidden;\n }\n\n img {\n display: block;\n width: ${imageDimensions.width}px;\n height: ${imageDimensions.height}px;\n max-width: none;\n transform: translateX(-${crop.x}px);\n user-select: none;\n -webkit-user-drag: none;\n }\n </style>\n </head>\n <body>\n <img src=\"${escapeXmlAttributeValue(getRelativeOriginalImageSrc(originalUri))}\" alt=\"\" />\n </body>\n</html>`\n}\n\nconst generatePageSpreadSplitResource = async ({\n archive,\n resourcePath,\n}: {\n archive: Archive\n resourcePath: string\n}): Promise<HookResource | undefined> => {\n const virtualResource = parseVirtualPageSpreadResourcePath(resourcePath)\n\n if (virtualResource === undefined) return undefined\n\n const file = archive.records.find(\n (file) => file.uri === virtualResource.originalUri && !file.dir,\n )\n\n if (file === undefined || file.dir) {\n throw new Error(\n `no source file found for virtual page spread resourcePath:${resourcePath}`,\n )\n }\n\n const archiveCache = imageDimensionsCache.get(archive) ?? new Map()\n\n if (!imageDimensionsCache.has(archive)) {\n imageDimensionsCache.set(archive, archiveCache)\n }\n\n const imageDimensions =\n archiveCache.get(virtualResource.originalUri) ??\n (await readImageDimensions(await file.blob()))\n\n archiveCache.set(virtualResource.originalUri, imageDimensions)\n\n const body = createPageSpreadSplitXhtml({\n cropSide: virtualResource.cropSide,\n imageDimensions,\n originalUri: virtualResource.originalUri,\n })\n\n return {\n body,\n params: {\n contentType: PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n },\n }\n}\n\nexport const pageSpreadSplitResourceHook =\n ({ archive, resourcePath }: { archive: Archive; resourcePath: string }) =>\n async (resource: HookResource): Promise<HookResource> => {\n const pageSpreadResource = await generatePageSpreadSplitResource({\n archive,\n resourcePath,\n })\n\n if (pageSpreadResource === undefined) return resource\n\n return {\n ...resource,\n ...pageSpreadResource,\n params: {\n ...resource.params,\n ...pageSpreadResource.params,\n },\n }\n }\n","import {\n getEpubCfiSpineItemref,\n updateEpubCfiSpineItemref,\n} from \"@prose-reader/cfi\"\nimport type { Reader } from \"@prose-reader/core\"\nimport type { Manifest } from \"@prose-reader/shared\"\nimport { createXmlSafeId } from \"@prose-reader/streamer\"\nimport { parseVirtualPageSpreadResourcePath } from \"./pageSpreadSplitResource\"\n\nconst VIRTUAL_SPINE_ID_EXTENSION = \"vnd.prose-reader.cbz.virtual-spine-id\"\n\ntype SpineItem = Manifest[\"spineItems\"][number]\n\nexport type CbzEnhancerAPI = {\n __PROSE_READER_ENHANCER_CBZ: true\n}\n\nconst decodeURIComponentSafe = (value: string) => {\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n}\n\nconst decodeURISafe = (value: string) => {\n try {\n return decodeURI(value)\n } catch {\n return value\n }\n}\n\nconst parseVirtualPageSpreadFromHref = (href: string) => {\n const virtualResource =\n parseVirtualPageSpreadResourcePath(href) ??\n parseVirtualPageSpreadResourcePath(decodeURISafe(href))\n\n if (!virtualResource) return undefined\n\n return {\n ...virtualResource,\n originalUri: decodeURIComponentSafe(virtualResource.originalUri),\n }\n}\n\nconst getOriginalSpineIndex = (reader: Reader, originalUri: string) => {\n const seenVirtualOriginalUris = new Set<string>()\n let originalSpineIndex = 0\n\n for (const spineItem of reader.spineItemsManager.items) {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.item.href)\n\n if (!virtualResource) {\n originalSpineIndex++\n continue\n }\n\n if (virtualResource.originalUri === originalUri) {\n return originalSpineIndex\n }\n\n if (!seenVirtualOriginalUris.has(virtualResource.originalUri)) {\n seenVirtualOriginalUris.add(virtualResource.originalUri)\n originalSpineIndex++\n }\n }\n\n return undefined\n}\n\nconst restoreOriginalSpineReference = (\n reader: Reader,\n cfi: string,\n spineItem: SpineItem,\n) => {\n const virtualResource = parseVirtualPageSpreadFromHref(spineItem.href)\n\n if (!virtualResource) return undefined\n\n const originalSpineIndex = getOriginalSpineIndex(\n reader,\n virtualResource.originalUri,\n )\n\n if (originalSpineIndex === undefined) return undefined\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: encodeURIComponent(spineItem.id),\n },\n spineId: createXmlSafeId(virtualResource.originalUri),\n spineIndex: originalSpineIndex,\n })\n}\n\nconst restoreVirtualSpineReference = (reader: Reader, cfi: string) => {\n const virtualSpineId =\n getEpubCfiSpineItemref(cfi)?.extensions?.[VIRTUAL_SPINE_ID_EXTENSION]\n\n if (!virtualSpineId) return undefined\n\n const virtualSpineItem = reader.spineItemsManager.get(\n decodeURIComponentSafe(virtualSpineId),\n )\n\n if (\n !virtualSpineItem ||\n !parseVirtualPageSpreadFromHref(virtualSpineItem.item.href)\n ) {\n return undefined\n }\n\n return updateEpubCfiSpineItemref(cfi, {\n extensions: {\n [VIRTUAL_SPINE_ID_EXTENSION]: undefined,\n },\n spineId: virtualSpineItem.item.id,\n spineIndex: virtualSpineItem.item.index,\n })\n}\n\nexport const cbzEnhancer =\n <InheritOptions, InheritOutput extends Reader>(\n next: (options: InheritOptions) => InheritOutput,\n ) =>\n (options: InheritOptions): InheritOutput & CbzEnhancerAPI => {\n const reader = next(options)\n\n const unregisterGenerateHook = reader.hookManager.register(\n \"cfi.afterGenerate\",\n ({ cfi, spineItem }) =>\n restoreOriginalSpineReference(reader, cfi, spineItem),\n )\n\n const unregisterResolveHook = reader.hookManager.register(\n \"cfi.beforeResolve\",\n ({ cfi }) => restoreVirtualSpineReference(reader, cfi),\n )\n\n const destroy = () => {\n unregisterGenerateHook()\n unregisterResolveHook()\n reader.destroy()\n }\n\n return {\n ...reader,\n __PROSE_READER_ENHANCER_CBZ: true,\n destroy,\n }\n }\n","import { parseContentType } from \"@prose-reader/shared\"\nimport type { Archive } from \"@prose-reader/streamer\"\n\nexport const CBZ_MIME_TYPES: ReadonlySet<string> = new Set([\n \"application/vnd.comicbook+zip\",\n \"application/x-cbz\",\n])\n\nconst hasCbzExtension = (name: string) => name.toLowerCase().endsWith(\".cbz\")\n\nexport const isCbzArchive = (archive: Archive) => {\n const mimeType = parseContentType(archive.encodingFormat ?? \"\")\n\n if (mimeType && CBZ_MIME_TYPES.has(mimeType)) return true\n if (hasCbzExtension(archive.filename)) return true\n\n return false\n}\n","import type { StreamerManifestHookFactory } from \"@prose-reader/streamer\"\nimport { isCbzArchive } from \"./isCbzArchive\"\n\nexport const detectReadingDirectionManifest: StreamerManifestHookFactory =\n ({ archive }) =>\n (manifest) => {\n if (manifest.readingDirection !== undefined) return manifest\n if (!isCbzArchive(archive)) return manifest\n\n return {\n ...manifest,\n readingDirection: \"rtl\",\n }\n }\n","import type {\n StreamerManifestHookFactory,\n StreamerResourceHookFactory,\n} from \"@prose-reader/streamer\"\nimport { detectReadingDirectionManifest } from \"./detectReadingDirectionManifest\"\nimport { pageSpreadSplit } from \"./pageSpreadSplitManifest\"\nimport { pageSpreadSplitResourceHook } from \"./pageSpreadSplitResource\"\n\nexport const streamerHooks: {\n manifest: {\n content: StreamerManifestHookFactory[]\n spine: StreamerManifestHookFactory[]\n }\n resource: StreamerResourceHookFactory[]\n} = {\n manifest: {\n content: [detectReadingDirectionManifest],\n spine: [pageSpreadSplit],\n },\n resource: [pageSpreadSplitResourceHook],\n}\n\nexport { detectReadingDirectionManifest } from \"./detectReadingDirectionManifest\"\nexport { CBZ_MIME_TYPES, isCbzArchive } from \"./isCbzArchive\"\n\nexport {\n buildVirtualPageSpreadResourcePath,\n detectPageSpreadFromBasename,\n isPageSpreadSplitSupportedArchiveRecord,\n isPageSpreadSplitSupportedImage,\n PAGE_SPREAD_RESOURCE_PREFIX,\n PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE,\n type PageSpreadCropSide,\n pageSpreadSplit,\n type VirtualPageSpreadResource,\n} from \"./pageSpreadSplitManifest\"\n\nexport {\n createPageSpreadSplitXhtml,\n pageSpreadSplitResourceHook,\n parseVirtualPageSpreadResourcePath,\n} from \"./pageSpreadSplitResource\"\n"],"names":["spreadPropertiesForSide","side","naturalSpreadSideAtIndex","index","readingDirection","isEvenSlot","openingPageSideForParityShift","isSpreadPair","firstItem","secondItem","firstSide","getItemSpreadPosition","secondSide","isSpreadPairNaturallyAligned","firstItemIndex","alignSpineItemsForSpreadParity","spineItems","openingItem","numberFromPageLabel","label","value","detectPageLabelsFromBasename","basenameWithoutExtension","explicitPageRangeMatch","detectPageSpreadFromBasename","basename","match","firstPageLabel","secondPageLabel","firstPageNumber","secondPageNumber","PAGE_SPREAD_RESOURCE_PREFIX","PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE","supportedImageMediaTypes","isPageSpreadSplitSupportedImage","mimeType","encodeOriginalUriSegment","uri","createManifestResourceHref","baseUrl","resourcePath","hrefBaseUrl","hasOpfExtension","path","isArchiveEpub","archive","file","buildVirtualPageSpreadResourcePath","cropSide","originalUri","cropSidesInReadingOrder","createVirtualSpineItem","originalSpineItem","progressionWeight","createXmlSafeId","createVirtualManifestItem","href","id","mediaType","getArchiveRecordForManifestItem","spineItem","hrefCandidates","decodeManifestHref","resourcePathCandidates","getResourcePathCandidates","item","normalizeBaseUrl","candidates","normalizedBaseUrl","mediaTypeFromArchiveRecord","record","parseContentType","detectMimeTypeFromName","mediaTypeFromArchiveRecordResourcePath","isPageSpreadSplitSupportedArchiveRecord","resourcePathMediaType","pageSpreadSplit","manifest","virtualManifestItems","archiveRecord","detected","firstCropSide","secondCropSide","splitProgressionWeight","firstSpineItem","secondSpineItem","alignedSpineItems","imageDimensionsCache","decodeOriginalUriSegment","encoded","parseVirtualPageSpreadResourcePath","prefixIndex","parts","encodedOriginalUri","cropFileName","cropRectForSide","imageHeight","imageWidth","leftWidth","rightWidth","getRelativeOriginalImageSrc","readImageDimensions","source","bitmap","createPageSpreadSplitXhtml","imageDimensions","crop","escapeXmlAttributeValue","generatePageSpreadSplitResource","virtualResource","archiveCache","pageSpreadSplitResourceHook","resource","pageSpreadResource","VIRTUAL_SPINE_ID_EXTENSION","decodeURIComponentSafe","decodeURISafe","parseVirtualPageSpreadFromHref","getOriginalSpineIndex","reader","seenVirtualOriginalUris","originalSpineIndex","restoreOriginalSpineReference","cfi","updateEpubCfiSpineItemref","restoreVirtualSpineReference","virtualSpineId","getEpubCfiSpineItemref","virtualSpineItem","cbzEnhancer","next","options","unregisterGenerateHook","unregisterResolveHook","CBZ_MIME_TYPES","hasCbzExtension","name","isCbzArchive","detectReadingDirectionManifest","streamerHooks"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Archive } from '@prose-reader/streamer';
2
+ export declare const CBZ_MIME_TYPES: ReadonlySet<string>;
3
+ export declare const isCbzArchive: (archive: Archive) => boolean;
@@ -1,9 +1,12 @@
1
1
  import { StreamerManifestHookFactory, StreamerResourceHookFactory } from '@prose-reader/streamer';
2
2
  export declare const streamerHooks: {
3
3
  manifest: {
4
+ content: StreamerManifestHookFactory[];
4
5
  spine: StreamerManifestHookFactory[];
5
6
  };
6
7
  resource: StreamerResourceHookFactory[];
7
8
  };
9
+ export { detectReadingDirectionManifest } from './detectReadingDirectionManifest';
10
+ export { CBZ_MIME_TYPES, isCbzArchive } from './isCbzArchive';
8
11
  export { buildVirtualPageSpreadResourcePath, detectPageSpreadFromBasename, isPageSpreadSplitSupportedArchiveRecord, isPageSpreadSplitSupportedImage, PAGE_SPREAD_RESOURCE_PREFIX, PAGE_SPREAD_SPLIT_DOCUMENT_MEDIA_TYPE, type PageSpreadCropSide, pageSpreadSplit, type VirtualPageSpreadResource, } from './pageSpreadSplitManifest';
9
12
  export { createPageSpreadSplitXhtml, pageSpreadSplitResourceHook, parseVirtualPageSpreadResourcePath, } from './pageSpreadSplitResource';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prose-reader/cbz",
3
- "version": "1.300.0",
3
+ "version": "1.303.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "/dist"
@@ -36,5 +36,5 @@
36
36
  "@prose-reader/shared": "^1.296.0",
37
37
  "@prose-reader/streamer": "^1.296.0"
38
38
  },
39
- "gitHead": "5c5abbe4e50639e6f620eae1d4f4e51104b7cc47"
39
+ "gitHead": "dddc06869ba852fa280e050c1c1929433bb91c5e"
40
40
  }