@prose-reader/cbz 1.301.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.
- package/dist/alignSpineItemsForSpreadParity.d.ts +17 -0
- package/dist/detectReadingDirectionManifest.d.ts +2 -0
- package/dist/detectReadingDirectionManifest.test.d.ts +1 -0
- package/dist/index.js +205 -140
- package/dist/index.js.map +1 -1
- package/dist/index.umd.cjs +3 -3
- package/dist/index.umd.cjs.map +1 -1
- package/dist/isCbzArchive.d.ts +3 -0
- package/dist/streamer.d.ts +3 -0
- package/package.json +2 -2
|
@@ -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 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,35 +1,84 @@
|
|
|
1
|
-
import { updateEpubCfiSpineItemref as
|
|
2
|
-
import { createXmlSafeId as
|
|
3
|
-
import { detectMimeTypeFromName as
|
|
4
|
-
const
|
|
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) => {
|
|
5
54
|
const t = Number.parseInt(e, 10);
|
|
6
|
-
if (Number.isFinite(t) && !(t < 0 || t >
|
|
55
|
+
if (Number.isFinite(t) && !(t < 0 || t > B))
|
|
7
56
|
return t;
|
|
8
|
-
},
|
|
57
|
+
}, D = (e) => {
|
|
9
58
|
const t = /(?:^|[\s._(-]|\[)p\s*(\d{1,5})\s*[-_~]\s*(?:p\s*)?(\d{1,5})(?=$|[^\d])/i.exec(
|
|
10
59
|
e
|
|
11
60
|
);
|
|
12
61
|
return t || /(?:^|[\s._(]|\[)(0\d{1,4})\s*[-_~]\s*(0\d{1,4})(?=$|[^\d])/i.exec(
|
|
13
62
|
e
|
|
14
63
|
);
|
|
15
|
-
},
|
|
16
|
-
const t = e.replace(/\.[^.]+$/, ""), r =
|
|
64
|
+
}, k = (e) => {
|
|
65
|
+
const t = e.replace(/\.[^.]+$/, ""), r = D(t);
|
|
17
66
|
if (!r) return;
|
|
18
|
-
const [, i,
|
|
19
|
-
if (i === void 0 ||
|
|
67
|
+
const [, i, n] = r;
|
|
68
|
+
if (i === void 0 || n === void 0)
|
|
20
69
|
return;
|
|
21
|
-
const
|
|
22
|
-
if (!(
|
|
70
|
+
const o = R(i), a = R(n);
|
|
71
|
+
if (!(o === void 0 || a === void 0) && a === o + 1)
|
|
23
72
|
return {
|
|
24
73
|
firstPageLabel: i,
|
|
25
|
-
secondPageLabel:
|
|
74
|
+
secondPageLabel: n
|
|
26
75
|
};
|
|
27
|
-
},
|
|
76
|
+
}, _ = "__prose-reader__/page-spread", U = "application/xhtml+xml", z = /* @__PURE__ */ new Set([
|
|
28
77
|
"image/jpg",
|
|
29
78
|
"image/jpeg",
|
|
30
79
|
"image/png",
|
|
31
80
|
"image/webp"
|
|
32
|
-
]),
|
|
81
|
+
]), I = (e) => e === void 0 ? !1 : z.has(e), H = (e) => encodeURIComponent(e), V = ({
|
|
33
82
|
baseUrl: e = "",
|
|
34
83
|
resourcePath: t
|
|
35
84
|
}) => {
|
|
@@ -37,33 +86,33 @@ const C = 2e3, m = (e) => {
|
|
|
37
86
|
return encodeURI(t);
|
|
38
87
|
const r = e ? `${e}${e.endsWith("/") ? "" : "/"}` : "file://";
|
|
39
88
|
return encodeURI(`${r}${t}`);
|
|
40
|
-
},
|
|
41
|
-
(t) => !t.dir && (
|
|
42
|
-
),
|
|
89
|
+
}, P = (e) => e.toLowerCase().endsWith(".opf"), X = (e) => e.records.some(
|
|
90
|
+
(t) => !t.dir && (P(t.basename) || P(t.uri))
|
|
91
|
+
), G = ({
|
|
43
92
|
cropSide: e,
|
|
44
93
|
originalUri: t
|
|
45
|
-
}) => `${
|
|
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 = ({
|
|
46
95
|
baseUrl: e,
|
|
47
96
|
cropSide: t,
|
|
48
97
|
label: r,
|
|
49
98
|
originalSpineItem: i,
|
|
50
|
-
originalUri:
|
|
51
|
-
progressionWeight:
|
|
99
|
+
originalUri: n,
|
|
100
|
+
progressionWeight: o
|
|
52
101
|
}) => {
|
|
53
|
-
const
|
|
102
|
+
const a = G({
|
|
54
103
|
cropSide: t,
|
|
55
|
-
originalUri:
|
|
104
|
+
originalUri: n
|
|
56
105
|
});
|
|
57
106
|
return {
|
|
58
107
|
...i,
|
|
59
|
-
id:
|
|
60
|
-
href:
|
|
61
|
-
mediaType:
|
|
62
|
-
progressionWeight:
|
|
108
|
+
id: y(`${i.id}.${r}`),
|
|
109
|
+
href: V({ baseUrl: e, resourcePath: a }),
|
|
110
|
+
mediaType: U,
|
|
111
|
+
progressionWeight: o,
|
|
63
112
|
renditionLayout: "pre-paginated",
|
|
64
|
-
...
|
|
113
|
+
...j(t)
|
|
65
114
|
};
|
|
66
|
-
},
|
|
115
|
+
}, x = ({
|
|
67
116
|
href: e,
|
|
68
117
|
id: t,
|
|
69
118
|
mediaType: r
|
|
@@ -71,104 +120,107 @@ const C = 2e3, m = (e) => {
|
|
|
71
120
|
href: e,
|
|
72
121
|
id: t,
|
|
73
122
|
mediaType: r
|
|
74
|
-
}),
|
|
123
|
+
}), Z = ({
|
|
75
124
|
archive: e,
|
|
76
125
|
baseUrl: t,
|
|
77
126
|
spineItem: r
|
|
78
127
|
}) => {
|
|
79
|
-
const i = [r.href,
|
|
80
|
-
i.flatMap((
|
|
128
|
+
const i = [r.href, q(r.href)], n = new Set(
|
|
129
|
+
i.flatMap((o) => K(o, t))
|
|
81
130
|
);
|
|
82
131
|
return e.records.find(
|
|
83
|
-
(
|
|
132
|
+
(o) => !o.dir && n.has(o.uri)
|
|
84
133
|
);
|
|
85
|
-
},
|
|
134
|
+
}, q = (e) => {
|
|
86
135
|
try {
|
|
87
136
|
return decodeURI(e);
|
|
88
137
|
} catch {
|
|
89
138
|
return e;
|
|
90
139
|
}
|
|
91
|
-
},
|
|
140
|
+
}, J = (e) => e.endsWith("/") ? e : `${e}/`, K = (e, t) => {
|
|
92
141
|
const r = [e];
|
|
93
142
|
if (e.startsWith("file://") && r.push(e.slice(7)), t) {
|
|
94
|
-
const i =
|
|
143
|
+
const i = J(t);
|
|
95
144
|
e.startsWith(i) && r.push(e.slice(i.length));
|
|
96
145
|
}
|
|
97
146
|
return r;
|
|
98
|
-
},
|
|
147
|
+
}, Q = (e) => b(e?.encodingFormat ?? "") || g(e?.basename ?? ""), ee = (e) => g(e.uri) || g(e.basename), te = (e) => {
|
|
99
148
|
if (e === void 0 || e.dir) return !1;
|
|
100
|
-
const t =
|
|
101
|
-
return
|
|
102
|
-
},
|
|
103
|
-
if (
|
|
104
|
-
const i = [],
|
|
105
|
-
const s =
|
|
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({
|
|
106
155
|
archive: e,
|
|
107
156
|
baseUrl: t,
|
|
108
|
-
spineItem:
|
|
157
|
+
spineItem: a
|
|
109
158
|
});
|
|
110
|
-
if (!
|
|
111
|
-
return [
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
114
|
-
const [
|
|
159
|
+
if (!te(s))
|
|
160
|
+
return [a];
|
|
161
|
+
const c = k(s.basename);
|
|
162
|
+
if (c === void 0) return [a];
|
|
163
|
+
const [C, $] = Y(
|
|
115
164
|
r.readingDirection
|
|
116
|
-
),
|
|
165
|
+
), f = a.progressionWeight !== void 0 ? a.progressionWeight / 2 : void 0, m = w({
|
|
117
166
|
baseUrl: t,
|
|
118
|
-
cropSide:
|
|
119
|
-
label:
|
|
120
|
-
originalSpineItem:
|
|
167
|
+
cropSide: C,
|
|
168
|
+
label: c.firstPageLabel,
|
|
169
|
+
originalSpineItem: a,
|
|
121
170
|
originalUri: s.uri,
|
|
122
|
-
progressionWeight:
|
|
123
|
-
}),
|
|
171
|
+
progressionWeight: f
|
|
172
|
+
}), S = w({
|
|
124
173
|
baseUrl: t,
|
|
125
|
-
cropSide:
|
|
126
|
-
label:
|
|
127
|
-
originalSpineItem:
|
|
174
|
+
cropSide: $,
|
|
175
|
+
label: c.secondPageLabel,
|
|
176
|
+
originalSpineItem: a,
|
|
128
177
|
originalUri: s.uri,
|
|
129
|
-
progressionWeight:
|
|
178
|
+
progressionWeight: f
|
|
130
179
|
});
|
|
131
180
|
return i.push(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
), [
|
|
181
|
+
x(m),
|
|
182
|
+
x(S)
|
|
183
|
+
), [m, S];
|
|
184
|
+
}), o = W({
|
|
185
|
+
readingDirection: r.readingDirection,
|
|
186
|
+
spineItems: n
|
|
135
187
|
});
|
|
136
|
-
return i.length === 0 ? r : {
|
|
188
|
+
return i.length === 0 && o === n ? r : {
|
|
137
189
|
...r,
|
|
138
|
-
spineItems: o.map((
|
|
139
|
-
...
|
|
190
|
+
spineItems: o.map((a, s) => ({
|
|
191
|
+
...a,
|
|
140
192
|
index: s
|
|
141
193
|
})),
|
|
142
194
|
items: [...r.items, ...i]
|
|
143
195
|
};
|
|
144
|
-
},
|
|
196
|
+
}, p = /* @__PURE__ */ new WeakMap(), ie = (e) => {
|
|
145
197
|
try {
|
|
146
198
|
return decodeURIComponent(e);
|
|
147
199
|
} catch {
|
|
148
200
|
return;
|
|
149
201
|
}
|
|
150
|
-
},
|
|
151
|
-
const t = e.indexOf(`${
|
|
202
|
+
}, u = (e) => {
|
|
203
|
+
const t = e.indexOf(`${_}/`);
|
|
152
204
|
if (t < 0) return;
|
|
153
|
-
const i = e.slice(t).split("/"),
|
|
154
|
-
if (i.length !== 4 || i[0] !== "__prose-reader__" || i[1] !== "page-spread" ||
|
|
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)
|
|
155
207
|
return;
|
|
156
|
-
const
|
|
157
|
-
if (
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
208
|
+
const a = o.split(".")[0];
|
|
209
|
+
if (a !== "left" && a !== "right") return;
|
|
210
|
+
const s = ie(n);
|
|
211
|
+
if (s !== void 0)
|
|
160
212
|
return {
|
|
161
|
-
originalUri:
|
|
162
|
-
cropSide:
|
|
213
|
+
originalUri: s,
|
|
214
|
+
cropSide: a
|
|
163
215
|
};
|
|
164
|
-
},
|
|
216
|
+
}, ne = ({
|
|
165
217
|
cropSide: e,
|
|
166
218
|
imageHeight: t,
|
|
167
219
|
imageWidth: r
|
|
168
220
|
}) => {
|
|
169
|
-
const i = Math.floor(r / 2),
|
|
170
|
-
return e === "left" ? { x: 0, width: i, height: t } : { x: i, width:
|
|
171
|
-
},
|
|
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) => {
|
|
172
224
|
if (typeof createImageBitmap != "function")
|
|
173
225
|
throw new Error("Page spread XHTML generation requires createImageBitmap");
|
|
174
226
|
const t = await createImageBitmap(e);
|
|
@@ -180,14 +232,14 @@ const C = 2e3, m = (e) => {
|
|
|
180
232
|
} finally {
|
|
181
233
|
t.close();
|
|
182
234
|
}
|
|
183
|
-
},
|
|
235
|
+
}, se = ({
|
|
184
236
|
cropSide: e,
|
|
185
237
|
imageDimensions: t,
|
|
186
238
|
originalUri: r
|
|
187
239
|
}) => {
|
|
188
240
|
if (t.width < 2)
|
|
189
241
|
throw new Error("Page spread image is too narrow to split");
|
|
190
|
-
const i =
|
|
242
|
+
const i = ne({
|
|
191
243
|
cropSide: e,
|
|
192
244
|
imageHeight: t.height,
|
|
193
245
|
imageWidth: t.width
|
|
@@ -217,37 +269,37 @@ const C = 2e3, m = (e) => {
|
|
|
217
269
|
</style>
|
|
218
270
|
</head>
|
|
219
271
|
<body>
|
|
220
|
-
<img src="${
|
|
272
|
+
<img src="${F(oe(r))}" alt="" />
|
|
221
273
|
</body>
|
|
222
274
|
</html>`;
|
|
223
|
-
},
|
|
275
|
+
}, de = async ({
|
|
224
276
|
archive: e,
|
|
225
277
|
resourcePath: t
|
|
226
278
|
}) => {
|
|
227
|
-
const r =
|
|
279
|
+
const r = u(t);
|
|
228
280
|
if (r === void 0) return;
|
|
229
281
|
const i = e.records.find(
|
|
230
|
-
(
|
|
282
|
+
(s) => s.uri === r.originalUri && !s.dir
|
|
231
283
|
);
|
|
232
284
|
if (i === void 0 || i.dir)
|
|
233
285
|
throw new Error(
|
|
234
286
|
`no source file found for virtual page spread resourcePath:${t}`
|
|
235
287
|
);
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
return
|
|
240
|
-
body:
|
|
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({
|
|
241
293
|
cropSide: r.cropSide,
|
|
242
|
-
imageDimensions:
|
|
294
|
+
imageDimensions: o,
|
|
243
295
|
originalUri: r.originalUri
|
|
244
296
|
}),
|
|
245
297
|
params: {
|
|
246
|
-
contentType:
|
|
298
|
+
contentType: U
|
|
247
299
|
}
|
|
248
300
|
};
|
|
249
|
-
},
|
|
250
|
-
const i = await
|
|
301
|
+
}, ce = ({ archive: e, resourcePath: t }) => async (r) => {
|
|
302
|
+
const i = await de({
|
|
251
303
|
archive: e,
|
|
252
304
|
resourcePath: t
|
|
253
305
|
});
|
|
@@ -259,100 +311,113 @@ const C = 2e3, m = (e) => {
|
|
|
259
311
|
...i.params
|
|
260
312
|
}
|
|
261
313
|
};
|
|
262
|
-
},
|
|
314
|
+
}, l = "vnd.prose-reader.cbz.virtual-spine-id", M = (e) => {
|
|
263
315
|
try {
|
|
264
316
|
return decodeURIComponent(e);
|
|
265
317
|
} catch {
|
|
266
318
|
return e;
|
|
267
319
|
}
|
|
268
|
-
},
|
|
320
|
+
}, pe = (e) => {
|
|
269
321
|
try {
|
|
270
322
|
return decodeURI(e);
|
|
271
323
|
} catch {
|
|
272
324
|
return e;
|
|
273
325
|
}
|
|
274
|
-
},
|
|
275
|
-
const t =
|
|
326
|
+
}, h = (e) => {
|
|
327
|
+
const t = u(e) ?? u(pe(e));
|
|
276
328
|
if (t)
|
|
277
329
|
return {
|
|
278
330
|
...t,
|
|
279
|
-
originalUri:
|
|
331
|
+
originalUri: M(t.originalUri)
|
|
280
332
|
};
|
|
281
|
-
},
|
|
333
|
+
}, ge = (e, t) => {
|
|
282
334
|
const r = /* @__PURE__ */ new Set();
|
|
283
335
|
let i = 0;
|
|
284
|
-
for (const
|
|
285
|
-
const
|
|
286
|
-
if (!
|
|
336
|
+
for (const n of e.spineItemsManager.items) {
|
|
337
|
+
const o = h(n.item.href);
|
|
338
|
+
if (!o) {
|
|
287
339
|
i++;
|
|
288
340
|
continue;
|
|
289
341
|
}
|
|
290
|
-
if (
|
|
342
|
+
if (o.originalUri === t)
|
|
291
343
|
return i;
|
|
292
|
-
r.has(
|
|
344
|
+
r.has(o.originalUri) || (r.add(o.originalUri), i++);
|
|
293
345
|
}
|
|
294
|
-
},
|
|
295
|
-
const i =
|
|
346
|
+
}, ue = (e, t, r) => {
|
|
347
|
+
const i = h(r.href);
|
|
296
348
|
if (!i) return;
|
|
297
|
-
const
|
|
349
|
+
const n = ge(
|
|
298
350
|
e,
|
|
299
351
|
i.originalUri
|
|
300
352
|
);
|
|
301
|
-
if (
|
|
302
|
-
return
|
|
353
|
+
if (n !== void 0)
|
|
354
|
+
return E(t, {
|
|
303
355
|
extensions: {
|
|
304
|
-
[
|
|
356
|
+
[l]: encodeURIComponent(r.id)
|
|
305
357
|
},
|
|
306
|
-
spineId:
|
|
307
|
-
spineIndex:
|
|
358
|
+
spineId: y(i.originalUri),
|
|
359
|
+
spineIndex: n
|
|
308
360
|
});
|
|
309
|
-
},
|
|
310
|
-
const r =
|
|
361
|
+
}, le = (e, t) => {
|
|
362
|
+
const r = A(t)?.extensions?.[l];
|
|
311
363
|
if (!r) return;
|
|
312
364
|
const i = e.spineItemsManager.get(
|
|
313
|
-
|
|
365
|
+
M(r)
|
|
314
366
|
);
|
|
315
|
-
if (!(!i || !
|
|
316
|
-
return
|
|
367
|
+
if (!(!i || !h(i.item.href)))
|
|
368
|
+
return E(t, {
|
|
317
369
|
extensions: {
|
|
318
|
-
[
|
|
370
|
+
[l]: void 0
|
|
319
371
|
},
|
|
320
372
|
spineId: i.item.id,
|
|
321
373
|
spineIndex: i.item.index
|
|
322
374
|
});
|
|
323
|
-
},
|
|
375
|
+
}, Pe = (e) => (t) => {
|
|
324
376
|
const r = e(t), i = r.hookManager.register(
|
|
325
377
|
"cfi.afterGenerate",
|
|
326
|
-
({ cfi:
|
|
327
|
-
),
|
|
378
|
+
({ cfi: a, spineItem: s }) => ue(r, a, s)
|
|
379
|
+
), n = r.hookManager.register(
|
|
328
380
|
"cfi.beforeResolve",
|
|
329
|
-
({ cfi:
|
|
381
|
+
({ cfi: a }) => le(r, a)
|
|
330
382
|
);
|
|
331
383
|
return {
|
|
332
384
|
...r,
|
|
333
385
|
__PROSE_READER_ENHANCER_CBZ: !0,
|
|
334
386
|
destroy: () => {
|
|
335
|
-
i(),
|
|
387
|
+
i(), n(), r.destroy();
|
|
336
388
|
}
|
|
337
389
|
};
|
|
338
|
-
},
|
|
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 = {
|
|
339
400
|
manifest: {
|
|
340
|
-
|
|
401
|
+
content: [Se],
|
|
402
|
+
spine: [re]
|
|
341
403
|
},
|
|
342
|
-
resource: [
|
|
404
|
+
resource: [ce]
|
|
343
405
|
};
|
|
344
406
|
export {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
te as
|
|
355
|
-
|
|
356
|
-
|
|
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
|
|
357
422
|
};
|
|
358
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, createXmlSafeId } 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: 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 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 { 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 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","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","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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"}
|
package/dist/index.umd.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(
|
|
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="${
|
|
26
|
+
<img src="${c.escapeXmlAttributeValue(ne(r))}" alt="" />
|
|
27
27
|
</body>
|
|
28
|
-
</html>`},
|
|
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
|
package/dist/index.umd.cjs.map
CHANGED
|
@@ -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, createXmlSafeId } 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: 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 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 { 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 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","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","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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"}
|
package/dist/streamer.d.ts
CHANGED
|
@@ -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.
|
|
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": "
|
|
39
|
+
"gitHead": "dddc06869ba852fa280e050c1c1929433bb91c5e"
|
|
40
40
|
}
|