@ankhorage/expo-runtime 3.1.1 → 3.2.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/CHANGELOG.md +12 -0
- package/README.md +2 -1
- package/dist/createExpoRuntimeRegistry.d.ts.map +1 -1
- package/dist/createExpoRuntimeRegistry.js +5 -0
- package/dist/createExpoRuntimeRegistry.js.map +1 -1
- package/dist/expoRuntimePlanningMetadata.d.ts +9 -1
- package/dist/expoRuntimePlanningMetadata.d.ts.map +1 -1
- package/dist/expoRuntimePlanningMetadata.js +8 -0
- package/dist/expoRuntimePlanningMetadata.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/reader/ExpoReaderSurfaceAdapter.d.ts +4 -0
- package/dist/reader/ExpoReaderSurfaceAdapter.d.ts.map +1 -0
- package/dist/reader/ExpoReaderSurfaceAdapter.js +102 -0
- package/dist/reader/ExpoReaderSurfaceAdapter.js.map +1 -0
- package/dist/reader/ReaderDocumentError.d.ts +6 -0
- package/dist/reader/ReaderDocumentError.d.ts.map +1 -0
- package/dist/reader/ReaderDocumentError.js +9 -0
- package/dist/reader/ReaderDocumentError.js.map +1 -0
- package/dist/reader/ReaderViewport.d.ts +4 -0
- package/dist/reader/ReaderViewport.d.ts.map +1 -0
- package/dist/reader/ReaderViewport.js +212 -0
- package/dist/reader/ReaderViewport.js.map +1 -0
- package/dist/reader/drivers/EpubReaderDriver.d.ts +20 -0
- package/dist/reader/drivers/EpubReaderDriver.d.ts.map +1 -0
- package/dist/reader/drivers/EpubReaderDriver.js +259 -0
- package/dist/reader/drivers/EpubReaderDriver.js.map +1 -0
- package/dist/reader/drivers/PdfReaderDriver.d.ts +18 -0
- package/dist/reader/drivers/PdfReaderDriver.d.ts.map +1 -0
- package/dist/reader/drivers/PdfReaderDriver.js +198 -0
- package/dist/reader/drivers/PdfReaderDriver.js.map +1 -0
- package/dist/reader/epub/EpubArchive.d.ts +11 -0
- package/dist/reader/epub/EpubArchive.d.ts.map +1 -0
- package/dist/reader/epub/EpubArchive.js +65 -0
- package/dist/reader/epub/EpubArchive.js.map +1 -0
- package/dist/reader/epub/EpubArchiveFetcher.d.ts +11 -0
- package/dist/reader/epub/EpubArchiveFetcher.d.ts.map +1 -0
- package/dist/reader/epub/EpubArchiveFetcher.js +44 -0
- package/dist/reader/epub/EpubArchiveFetcher.js.map +1 -0
- package/dist/reader/epub/mediaTypes.d.ts +3 -0
- package/dist/reader/epub/mediaTypes.d.ts.map +1 -0
- package/dist/reader/epub/mediaTypes.js +26 -0
- package/dist/reader/epub/mediaTypes.js.map +1 -0
- package/dist/reader/epub/parseEpubPackage.d.ts +22 -0
- package/dist/reader/epub/parseEpubPackage.d.ts.map +1 -0
- package/dist/reader/epub/parseEpubPackage.js +98 -0
- package/dist/reader/epub/parseEpubPackage.js.map +1 -0
- package/dist/reader/epub/path.d.ts +4 -0
- package/dist/reader/epub/path.d.ts.map +1 -0
- package/dist/reader/epub/path.js +28 -0
- package/dist/reader/epub/path.js.map +1 -0
- package/dist/reader/epub/sanitizeEpubDocument.d.ts +3 -0
- package/dist/reader/epub/sanitizeEpubDocument.d.ts.map +1 -0
- package/dist/reader/epub/sanitizeEpubDocument.js +90 -0
- package/dist/reader/epub/sanitizeEpubDocument.js.map +1 -0
- package/dist/reader/loadReaderSourceAsync.d.ts +2 -0
- package/dist/reader/loadReaderSourceAsync.d.ts.map +1 -0
- package/dist/reader/loadReaderSourceAsync.js +49 -0
- package/dist/reader/loadReaderSourceAsync.js.map +1 -0
- package/dist/reader/readerViewportPresentation.d.ts +4 -0
- package/dist/reader/readerViewportPresentation.d.ts.map +1 -0
- package/dist/reader/readerViewportPresentation.js +26 -0
- package/dist/reader/readerViewportPresentation.js.map +1 -0
- package/dist/reader/types.d.ts +57 -0
- package/dist/reader/types.d.ts.map +1 -0
- package/dist/reader/types.js +2 -0
- package/dist/reader/types.js.map +1 -0
- package/dist/reader/validateEpubArchiveEntriesAsync.d.ts +3 -0
- package/dist/reader/validateEpubArchiveEntriesAsync.d.ts.map +1 -0
- package/dist/reader/validateEpubArchiveEntriesAsync.js +48 -0
- package/dist/reader/validateEpubArchiveEntriesAsync.js.map +1 -0
- package/package.json +11 -4
- package/src/createExpoRuntimeRegistry.test.tsx +9 -1
- package/src/createExpoRuntimeRegistry.tsx +7 -1
- package/src/expoRuntimePlanningMetadata.ts +9 -1
- package/src/index.ts +1 -0
- package/src/reader/ExpoReaderSurfaceAdapter.tsx +170 -0
- package/src/reader/ReaderDocumentError.ts +11 -0
- package/src/reader/ReaderViewport.tsx +319 -0
- package/src/reader/drivers/EpubReaderDriver.ts +309 -0
- package/src/reader/drivers/PdfReaderDriver.ts +253 -0
- package/src/reader/epub/EpubArchive.ts +83 -0
- package/src/reader/epub/EpubArchiveFetcher.ts +58 -0
- package/src/reader/epub/mediaTypes.ts +16 -0
- package/src/reader/epub/parseEpubPackage.test.ts +116 -0
- package/src/reader/epub/parseEpubPackage.ts +152 -0
- package/src/reader/epub/path.test.ts +22 -0
- package/src/reader/epub/path.ts +26 -0
- package/src/reader/epub/sanitizeEpubDocument.ts +121 -0
- package/src/reader/loadReaderSourceAsync.test.ts +52 -0
- package/src/reader/loadReaderSourceAsync.ts +59 -0
- package/src/reader/readerViewportPresentation.ts +28 -0
- package/src/reader/rendererPackages.test.ts +3 -1
- package/src/reader/types.ts +61 -0
- package/src/reader/validateEpubArchiveEntriesAsync.test.ts +142 -0
- package/src/reader/validateEpubArchiveEntriesAsync.ts +73 -0
- package/src/resolveExpoRuntimePlan.test.ts +36 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EpubArchive.d.ts","sourceRoot":"","sources":["../../../src/reader/epub/EpubArchive.ts"],"names":[],"mappings":"AAYA,qBAAa,WAAW;;IAItB,OAAO;WAKM,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAkB/E,KAAK,IAAI,SAAS,MAAM,EAAE;IAI1B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAY7D,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Uint8ArrayReader, Uint8ArrayWriter, ZipReader, } from '@zip.js/zip.js';
|
|
2
|
+
import { ReaderDocumentError } from '../ReaderDocumentError';
|
|
3
|
+
import { validateEpubArchiveEntriesAsync } from '../validateEpubArchiveEntriesAsync';
|
|
4
|
+
import { normalizeArchivePath } from './path';
|
|
5
|
+
export class EpubArchive {
|
|
6
|
+
#entries;
|
|
7
|
+
#reader;
|
|
8
|
+
constructor(reader, entries) {
|
|
9
|
+
this.#reader = reader;
|
|
10
|
+
this.#entries = createEntryMap(entries);
|
|
11
|
+
}
|
|
12
|
+
static async open(bytes, signal) {
|
|
13
|
+
const reader = new ZipReader(new Uint8ArrayReader(bytes), { strictness: 'strict' });
|
|
14
|
+
try {
|
|
15
|
+
const entries = await reader.getEntries({ strictness: 'strict' });
|
|
16
|
+
await validateEpubArchiveEntriesAsync(entries, signal);
|
|
17
|
+
if (entries.some((entry) => !entry.directory && entry.encrypted)) {
|
|
18
|
+
throw new ReaderDocumentError('protected-document', 'This EPUB uses content protection and cannot be opened.');
|
|
19
|
+
}
|
|
20
|
+
return new EpubArchive(reader, entries);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
await reader.close();
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
paths() {
|
|
28
|
+
return [...this.#entries.keys()];
|
|
29
|
+
}
|
|
30
|
+
has(path) {
|
|
31
|
+
return this.#entries.has(normalizeArchivePath(path));
|
|
32
|
+
}
|
|
33
|
+
async read(path, signal) {
|
|
34
|
+
const normalizedPath = normalizeArchivePath(path);
|
|
35
|
+
const entry = this.#entries.get(normalizedPath);
|
|
36
|
+
if (entry === undefined)
|
|
37
|
+
throw new Error(`Missing EPUB resource: ${normalizedPath}`);
|
|
38
|
+
return entry.getData(new Uint8ArrayWriter(), {
|
|
39
|
+
checkCrc32: true,
|
|
40
|
+
checkOverlappingEntry: true,
|
|
41
|
+
signal,
|
|
42
|
+
strictness: 'strict',
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async readText(path, signal) {
|
|
46
|
+
return new TextDecoder().decode(await this.read(path, signal));
|
|
47
|
+
}
|
|
48
|
+
async close() {
|
|
49
|
+
await this.#reader.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function createEntryMap(entries) {
|
|
53
|
+
const files = new Map();
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (entry.directory)
|
|
56
|
+
continue;
|
|
57
|
+
const normalizedPath = normalizeArchivePath(entry.filename);
|
|
58
|
+
if (files.has(normalizedPath)) {
|
|
59
|
+
throw new ReaderDocumentError('invalid-document', `The EPUB contains duplicate resource path "${normalizedPath}".`);
|
|
60
|
+
}
|
|
61
|
+
files.set(normalizedPath, entry);
|
|
62
|
+
}
|
|
63
|
+
return files;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=EpubArchive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EpubArchive.js","sourceRoot":"","sources":["../../../src/reader/epub/EpubArchive.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n type Entry,\n type FileEntry,\n Uint8ArrayReader,\n Uint8ArrayWriter,\n ZipReader,\n} from '@zip.js/zip.js';\n\nimport { ReaderDocumentError } from '../ReaderDocumentError';\nimport { validateEpubArchiveEntriesAsync } from '../validateEpubArchiveEntriesAsync';\nimport { normalizeArchivePath } from './path';\n\nexport class EpubArchive {\n readonly #entries: Map<string, FileEntry>;\n readonly #reader: ZipReader<Uint8Array>;\n\n private constructor(reader: ZipReader<Uint8Array>, entries: readonly Entry[]) {\n this.#reader = reader;\n this.#entries = createEntryMap(entries);\n }\n\n static async open(bytes: Uint8Array, signal: AbortSignal): Promise<EpubArchive> {\n const reader = new ZipReader(new Uint8ArrayReader(bytes), { strictness: 'strict' });\n try {\n const entries = await reader.getEntries({ strictness: 'strict' });\n await validateEpubArchiveEntriesAsync(entries, signal);\n if (entries.some((entry) => !entry.directory && entry.encrypted)) {\n throw new ReaderDocumentError(\n 'protected-document',\n 'This EPUB uses content protection and cannot be opened.',\n );\n }\n return new EpubArchive(reader, entries);\n } catch (error) {\n await reader.close();\n throw error;\n }\n }\n\n paths(): readonly string[] {\n return [...this.#entries.keys()];\n }\n\n has(path: string): boolean {\n return this.#entries.has(normalizeArchivePath(path));\n }\n\n async read(path: string, signal?: AbortSignal): Promise<Uint8Array> {\n const normalizedPath = normalizeArchivePath(path);\n const entry = this.#entries.get(normalizedPath);\n if (entry === undefined) throw new Error(`Missing EPUB resource: ${normalizedPath}`);\n return entry.getData(new Uint8ArrayWriter(), {\n checkCrc32: true,\n checkOverlappingEntry: true,\n signal,\n strictness: 'strict',\n });\n }\n\n async readText(path: string, signal?: AbortSignal): Promise<string> {\n return new TextDecoder().decode(await this.read(path, signal));\n }\n\n async close(): Promise<void> {\n await this.#reader.close();\n }\n}\n\nfunction createEntryMap(entries: readonly Entry[]): Map<string, FileEntry> {\n const files = new Map<string, FileEntry>();\n for (const entry of entries) {\n if (entry.directory) continue;\n const normalizedPath = normalizeArchivePath(entry.filename);\n if (files.has(normalizedPath)) {\n throw new ReaderDocumentError(\n 'invalid-document',\n `The EPUB contains duplicate resource path \"${normalizedPath}\".`,\n );\n }\n files.set(normalizedPath, entry);\n }\n return files;\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Link } from '@readium/shared';
|
|
2
|
+
import { type Fetcher, Resource } from '@readium/shared';
|
|
3
|
+
import type { EpubArchive } from './EpubArchive';
|
|
4
|
+
export declare class EpubArchiveFetcher implements Fetcher {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(archive: EpubArchive, links: readonly Link[], overrides: ReadonlyMap<string, Uint8Array>);
|
|
7
|
+
links(): Link[];
|
|
8
|
+
get(link: Link): Resource;
|
|
9
|
+
close(): void;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=EpubArchiveFetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EpubArchiveFetcher.d.ts","sourceRoot":"","sources":["../../../src/reader/epub/EpubArchiveFetcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,KAAK,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,qBAAa,kBAAmB,YAAW,OAAO;;gBAM9C,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC;IAO5C,KAAK,IAAI,IAAI,EAAE;IAIf,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,QAAQ;IAIzB,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Resource } from '@readium/shared';
|
|
2
|
+
export class EpubArchiveFetcher {
|
|
3
|
+
#archive;
|
|
4
|
+
#links;
|
|
5
|
+
#overrides;
|
|
6
|
+
constructor(archive, links, overrides) {
|
|
7
|
+
this.#archive = archive;
|
|
8
|
+
this.#links = [...links];
|
|
9
|
+
this.#overrides = overrides;
|
|
10
|
+
}
|
|
11
|
+
links() {
|
|
12
|
+
return [...this.#links];
|
|
13
|
+
}
|
|
14
|
+
get(link) {
|
|
15
|
+
return new EpubArchiveResource(this.#archive, link, this.#overrides.get(link.href));
|
|
16
|
+
}
|
|
17
|
+
close() {
|
|
18
|
+
void this.#archive.close();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class EpubArchiveResource extends Resource {
|
|
22
|
+
archive;
|
|
23
|
+
resourceLink;
|
|
24
|
+
override;
|
|
25
|
+
constructor(archive, resourceLink, override) {
|
|
26
|
+
super();
|
|
27
|
+
this.archive = archive;
|
|
28
|
+
this.resourceLink = resourceLink;
|
|
29
|
+
this.override = override;
|
|
30
|
+
}
|
|
31
|
+
link() {
|
|
32
|
+
return Promise.resolve(this.resourceLink);
|
|
33
|
+
}
|
|
34
|
+
async length() {
|
|
35
|
+
return (this.override ?? (await this.archive.read(this.resourceLink.href))).byteLength;
|
|
36
|
+
}
|
|
37
|
+
async read() {
|
|
38
|
+
return this.override ?? this.archive.read(this.resourceLink.href);
|
|
39
|
+
}
|
|
40
|
+
close() {
|
|
41
|
+
// Archive lifetime is owned by the driver.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=EpubArchiveFetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EpubArchiveFetcher.js","sourceRoot":"","sources":["../../../src/reader/epub/EpubArchiveFetcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIzD,MAAM,OAAO,kBAAkB;IACpB,QAAQ,CAAc;IACtB,MAAM,CAAS;IACf,UAAU,CAAkC;IAErD,YACE,OAAoB,EACpB,KAAsB,EACtB,SAA0C;QAE1C,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED,KAAK;QACH,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAED,GAAG,CAAC,IAAU;QACZ,OAAO,IAAI,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACtF,CAAC;IAED,KAAK;QACH,KAAK,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;CACF;AAED,MAAM,mBAAoB,SAAQ,QAAQ;IAE7B;IACA;IACA;IAHX,YACW,OAAoB,EACpB,YAAkB,EAClB,QAAgC;QAEzC,KAAK,EAAE,CAAC;QAJC,YAAO,GAAP,OAAO,CAAa;QACpB,iBAAY,GAAZ,YAAY,CAAM;QAClB,aAAQ,GAAR,QAAQ,CAAwB;IAG3C,CAAC;IAED,IAAI;QACF,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,MAAM;QACV,OAAO,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;IACzF,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IAED,KAAK;QACH,2CAA2C;IAC7C,CAAC;CACF","sourcesContent":["import type { Link } from '@readium/shared';\nimport { type Fetcher, Resource } from '@readium/shared';\n\nimport type { EpubArchive } from './EpubArchive';\n\nexport class EpubArchiveFetcher implements Fetcher {\n readonly #archive: EpubArchive;\n readonly #links: Link[];\n readonly #overrides: ReadonlyMap<string, Uint8Array>;\n\n constructor(\n archive: EpubArchive,\n links: readonly Link[],\n overrides: ReadonlyMap<string, Uint8Array>,\n ) {\n this.#archive = archive;\n this.#links = [...links];\n this.#overrides = overrides;\n }\n\n links(): Link[] {\n return [...this.#links];\n }\n\n get(link: Link): Resource {\n return new EpubArchiveResource(this.#archive, link, this.#overrides.get(link.href));\n }\n\n close(): void {\n void this.#archive.close();\n }\n}\n\nclass EpubArchiveResource extends Resource {\n constructor(\n readonly archive: EpubArchive,\n readonly resourceLink: Link,\n readonly override: Uint8Array | undefined,\n ) {\n super();\n }\n\n link(): Promise<Link> {\n return Promise.resolve(this.resourceLink);\n }\n\n async length(): Promise<number | undefined> {\n return (this.override ?? (await this.archive.read(this.resourceLink.href))).byteLength;\n }\n\n async read(): Promise<Uint8Array | undefined> {\n return this.override ?? this.archive.read(this.resourceLink.href);\n }\n\n close(): void {\n // Archive lifetime is owned by the driver.\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaTypes.d.ts","sourceRoot":"","sources":["../../../src/reader/epub/mediaTypes.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,gBAAgB,aAAkD,CAAC;AAEhF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAavD"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const HTML_MEDIA_TYPES = new Set(['application/xhtml+xml', 'text/html']);
|
|
2
|
+
export function inferEpubMediaType(path) {
|
|
3
|
+
const extension = path.split('.').pop()?.toLowerCase() ?? '';
|
|
4
|
+
if (extension === 'css')
|
|
5
|
+
return 'text/css';
|
|
6
|
+
if (extension === 'gif')
|
|
7
|
+
return 'image/gif';
|
|
8
|
+
if (extension === 'html')
|
|
9
|
+
return 'text/html';
|
|
10
|
+
if (extension === 'jpeg' || extension === 'jpg')
|
|
11
|
+
return 'image/jpeg';
|
|
12
|
+
if (extension === 'png')
|
|
13
|
+
return 'image/png';
|
|
14
|
+
if (extension === 'svg')
|
|
15
|
+
return 'image/svg+xml';
|
|
16
|
+
if (extension === 'ttf')
|
|
17
|
+
return 'font/ttf';
|
|
18
|
+
if (extension === 'woff')
|
|
19
|
+
return 'font/woff';
|
|
20
|
+
if (extension === 'woff2')
|
|
21
|
+
return 'font/woff2';
|
|
22
|
+
if (extension === 'xhtml')
|
|
23
|
+
return 'application/xhtml+xml';
|
|
24
|
+
return 'application/octet-stream';
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=mediaTypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mediaTypes.js","sourceRoot":"","sources":["../../../src/reader/epub/mediaTypes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,uBAAuB,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC7D,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,SAAS,KAAK,MAAM;QAAE,OAAO,WAAW,CAAC;IAC7C,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,YAAY,CAAC;IACrE,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,eAAe,CAAC;IAChD,IAAI,SAAS,KAAK,KAAK;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,SAAS,KAAK,MAAM;QAAE,OAAO,WAAW,CAAC;IAC7C,IAAI,SAAS,KAAK,OAAO;QAAE,OAAO,YAAY,CAAC;IAC/C,IAAI,SAAS,KAAK,OAAO;QAAE,OAAO,uBAAuB,CAAC;IAC1D,OAAO,0BAA0B,CAAC;AACpC,CAAC","sourcesContent":["export const HTML_MEDIA_TYPES = new Set(['application/xhtml+xml', 'text/html']);\n\nexport function inferEpubMediaType(path: string): string {\n const extension = path.split('.').pop()?.toLowerCase() ?? '';\n if (extension === 'css') return 'text/css';\n if (extension === 'gif') return 'image/gif';\n if (extension === 'html') return 'text/html';\n if (extension === 'jpeg' || extension === 'jpg') return 'image/jpeg';\n if (extension === 'png') return 'image/png';\n if (extension === 'svg') return 'image/svg+xml';\n if (extension === 'ttf') return 'font/ttf';\n if (extension === 'woff') return 'font/woff';\n if (extension === 'woff2') return 'font/woff2';\n if (extension === 'xhtml') return 'application/xhtml+xml';\n return 'application/octet-stream';\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EpubArchive } from './EpubArchive';
|
|
2
|
+
interface EpubPackageItem {
|
|
3
|
+
readonly href: string;
|
|
4
|
+
readonly mediaType: string;
|
|
5
|
+
readonly properties: readonly string[];
|
|
6
|
+
}
|
|
7
|
+
export interface ParsedEpubPackage {
|
|
8
|
+
readonly direction: 'ltr' | 'rtl';
|
|
9
|
+
readonly fixedLayout: boolean;
|
|
10
|
+
readonly language?: string;
|
|
11
|
+
readonly packagePath: string;
|
|
12
|
+
readonly readingOrder: readonly EpubPackageItem[];
|
|
13
|
+
readonly resources: readonly EpubPackageItem[];
|
|
14
|
+
readonly tableOfContents: readonly {
|
|
15
|
+
readonly href: string;
|
|
16
|
+
readonly title: string;
|
|
17
|
+
}[];
|
|
18
|
+
readonly title: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function parseEpubPackage(archive: EpubArchive, signal: AbortSignal): Promise<ParsedEpubPackage>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=parseEpubPackage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseEpubPackage.d.ts","sourceRoot":"","sources":["../../../src/reader/epub/parseEpubPackage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGjD,UAAU,eAAe;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IAClD,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IAC/C,QAAQ,CAAC,eAAe,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACvF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,iBAAiB,CAAC,CAmF5B"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* eslint-disable max-lines-per-function, complexity -- Package parsing validates one transactional document graph. */
|
|
2
|
+
import { ReaderDocumentError } from '../ReaderDocumentError';
|
|
3
|
+
import { resolveArchivePath } from './path';
|
|
4
|
+
export async function parseEpubPackage(archive, signal) {
|
|
5
|
+
const mimetype = (await archive.readText('mimetype', signal)).trim();
|
|
6
|
+
if (mimetype !== 'application/epub+zip') {
|
|
7
|
+
throw new ReaderDocumentError('invalid-document', 'The archive is not an EPUB document.');
|
|
8
|
+
}
|
|
9
|
+
if (archive.has('META-INF/encryption.xml')) {
|
|
10
|
+
const encryption = await archive.readText('META-INF/encryption.xml', signal);
|
|
11
|
+
if (elementsByLocalName(parseXml(encryption, 'EPUB encryption metadata'), 'EncryptedData')
|
|
12
|
+
.length > 0) {
|
|
13
|
+
throw new ReaderDocumentError('protected-document', 'This EPUB uses content protection and cannot be opened.');
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const container = parseXml(await archive.readText('META-INF/container.xml', signal), 'EPUB container');
|
|
17
|
+
const [rootfile] = elementsByLocalName(container, 'rootfile');
|
|
18
|
+
const packagePathValue = rootfile?.getAttribute('full-path');
|
|
19
|
+
if (!packagePathValue) {
|
|
20
|
+
throw new ReaderDocumentError('invalid-document', 'The EPUB container has no package document.');
|
|
21
|
+
}
|
|
22
|
+
const packagePath = resolveArchivePath('', packagePathValue);
|
|
23
|
+
const packageDocument = parseXml(await archive.readText(packagePath, signal), 'EPUB package');
|
|
24
|
+
const manifestItems = new Map();
|
|
25
|
+
for (const item of elementsByLocalName(packageDocument, 'item')) {
|
|
26
|
+
const id = item.getAttribute('id');
|
|
27
|
+
const href = item.getAttribute('href');
|
|
28
|
+
const mediaType = item.getAttribute('media-type');
|
|
29
|
+
if (!id || !href || !mediaType)
|
|
30
|
+
continue;
|
|
31
|
+
manifestItems.set(id, {
|
|
32
|
+
href: resolveArchivePath(packagePath, href),
|
|
33
|
+
mediaType,
|
|
34
|
+
properties: (item.getAttribute('properties') ?? '').split(/\s+/).filter(Boolean),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
const [spine] = elementsByLocalName(packageDocument, 'spine');
|
|
38
|
+
const readingOrder = elementsByLocalName(spine ?? packageDocument, 'itemref')
|
|
39
|
+
.map((itemref) => manifestItems.get(itemref.getAttribute('idref') ?? ''))
|
|
40
|
+
.filter((item) => item !== undefined);
|
|
41
|
+
if (readingOrder.length === 0) {
|
|
42
|
+
throw new ReaderDocumentError('invalid-document', 'The EPUB package has no readable spine.');
|
|
43
|
+
}
|
|
44
|
+
const metadata = elementsByLocalName(packageDocument, 'metadata')[0] ?? packageDocument.documentElement;
|
|
45
|
+
const title = elementText(metadata, 'title') ?? 'Untitled publication';
|
|
46
|
+
const language = elementText(metadata, 'language');
|
|
47
|
+
const fixedLayout = elementsByLocalName(metadata, 'meta').some((element) => element.getAttribute('property') === 'rendition:layout' &&
|
|
48
|
+
element.textContent.trim() === 'pre-paginated') || readingOrder.every((item) => item.properties.includes('rendition:layout-pre-paginated'));
|
|
49
|
+
const direction = spine?.getAttribute('page-progression-direction') === 'rtl' ? 'rtl' : 'ltr';
|
|
50
|
+
const readingPaths = new Set(readingOrder.map((item) => item.href));
|
|
51
|
+
const navigationItem = [...manifestItems.values()].find((item) => item.properties.includes('nav'));
|
|
52
|
+
const tableOfContents = navigationItem
|
|
53
|
+
? await parseNavigationDocument(archive, navigationItem.href, signal)
|
|
54
|
+
: [];
|
|
55
|
+
return {
|
|
56
|
+
direction,
|
|
57
|
+
fixedLayout,
|
|
58
|
+
language,
|
|
59
|
+
packagePath,
|
|
60
|
+
readingOrder,
|
|
61
|
+
resources: [...manifestItems.values()].filter((item) => !readingPaths.has(item.href)),
|
|
62
|
+
tableOfContents,
|
|
63
|
+
title,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function parseNavigationDocument(archive, navigationPath, signal) {
|
|
67
|
+
const document = parseXml(await archive.readText(navigationPath, signal), 'EPUB navigation');
|
|
68
|
+
const navigation = elementsByLocalName(document, 'nav').find((element) => (element.getAttribute('epub:type') ?? element.getAttribute('type') ?? '')
|
|
69
|
+
.split(/\s+/)
|
|
70
|
+
.includes('toc')) ?? elementsByLocalName(document, 'nav')[0];
|
|
71
|
+
if (!navigation)
|
|
72
|
+
return [];
|
|
73
|
+
return elementsByLocalName(navigation, 'a').flatMap((anchor) => {
|
|
74
|
+
const href = anchor.getAttribute('href');
|
|
75
|
+
const title = anchor.textContent.trim();
|
|
76
|
+
if (!href || !title)
|
|
77
|
+
return [];
|
|
78
|
+
const [resourceReference, fragment] = href.split('#', 2);
|
|
79
|
+
const resourcePath = resolveArchivePath(navigationPath, resourceReference ?? '');
|
|
80
|
+
return [{ href: fragment ? `${resourcePath}#${fragment}` : resourcePath, title }];
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function parseXml(source, label) {
|
|
84
|
+
const document = new DOMParser().parseFromString(source, 'application/xml');
|
|
85
|
+
if (elementsByLocalName(document, 'parsererror').length > 0) {
|
|
86
|
+
throw new ReaderDocumentError('invalid-document', `Invalid ${label}.`);
|
|
87
|
+
}
|
|
88
|
+
return document;
|
|
89
|
+
}
|
|
90
|
+
function elementsByLocalName(root, localName) {
|
|
91
|
+
return Array.from(root.getElementsByTagName('*')).filter((element) => element.localName === localName);
|
|
92
|
+
}
|
|
93
|
+
function elementText(root, localName) {
|
|
94
|
+
const [element] = elementsByLocalName(root, localName);
|
|
95
|
+
const value = element?.textContent.trim();
|
|
96
|
+
return value === '' ? undefined : value;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=parseEpubPackage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseEpubPackage.js","sourceRoot":"","sources":["../../../src/reader/epub/parseEpubPackage.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable max-lines-per-function, complexity -- Package parsing validates one transactional document graph. */\nimport { ReaderDocumentError } from '../ReaderDocumentError';\nimport type { EpubArchive } from './EpubArchive';\nimport { resolveArchivePath } from './path';\n\ninterface EpubPackageItem {\n readonly href: string;\n readonly mediaType: string;\n readonly properties: readonly string[];\n}\n\nexport interface ParsedEpubPackage {\n readonly direction: 'ltr' | 'rtl';\n readonly fixedLayout: boolean;\n readonly language?: string;\n readonly packagePath: string;\n readonly readingOrder: readonly EpubPackageItem[];\n readonly resources: readonly EpubPackageItem[];\n readonly tableOfContents: readonly { readonly href: string; readonly title: string }[];\n readonly title: string;\n}\n\nexport async function parseEpubPackage(\n archive: EpubArchive,\n signal: AbortSignal,\n): Promise<ParsedEpubPackage> {\n const mimetype = (await archive.readText('mimetype', signal)).trim();\n if (mimetype !== 'application/epub+zip') {\n throw new ReaderDocumentError('invalid-document', 'The archive is not an EPUB document.');\n }\n if (archive.has('META-INF/encryption.xml')) {\n const encryption = await archive.readText('META-INF/encryption.xml', signal);\n if (\n elementsByLocalName(parseXml(encryption, 'EPUB encryption metadata'), 'EncryptedData')\n .length > 0\n ) {\n throw new ReaderDocumentError(\n 'protected-document',\n 'This EPUB uses content protection and cannot be opened.',\n );\n }\n }\n\n const container = parseXml(\n await archive.readText('META-INF/container.xml', signal),\n 'EPUB container',\n );\n const [rootfile] = elementsByLocalName(container, 'rootfile');\n const packagePathValue = rootfile?.getAttribute('full-path');\n if (!packagePathValue) {\n throw new ReaderDocumentError(\n 'invalid-document',\n 'The EPUB container has no package document.',\n );\n }\n const packagePath = resolveArchivePath('', packagePathValue);\n const packageDocument = parseXml(await archive.readText(packagePath, signal), 'EPUB package');\n\n const manifestItems = new Map<string, EpubPackageItem>();\n for (const item of elementsByLocalName(packageDocument, 'item')) {\n const id = item.getAttribute('id');\n const href = item.getAttribute('href');\n const mediaType = item.getAttribute('media-type');\n if (!id || !href || !mediaType) continue;\n manifestItems.set(id, {\n href: resolveArchivePath(packagePath, href),\n mediaType,\n properties: (item.getAttribute('properties') ?? '').split(/\\s+/).filter(Boolean),\n });\n }\n\n const [spine] = elementsByLocalName(packageDocument, 'spine');\n const readingOrder = elementsByLocalName(spine ?? packageDocument, 'itemref')\n .map((itemref) => manifestItems.get(itemref.getAttribute('idref') ?? ''))\n .filter((item): item is EpubPackageItem => item !== undefined);\n if (readingOrder.length === 0) {\n throw new ReaderDocumentError('invalid-document', 'The EPUB package has no readable spine.');\n }\n\n const metadata =\n elementsByLocalName(packageDocument, 'metadata')[0] ?? packageDocument.documentElement;\n const title = elementText(metadata, 'title') ?? 'Untitled publication';\n const language = elementText(metadata, 'language');\n const fixedLayout =\n elementsByLocalName(metadata, 'meta').some(\n (element) =>\n element.getAttribute('property') === 'rendition:layout' &&\n element.textContent.trim() === 'pre-paginated',\n ) || readingOrder.every((item) => item.properties.includes('rendition:layout-pre-paginated'));\n const direction = spine?.getAttribute('page-progression-direction') === 'rtl' ? 'rtl' : 'ltr';\n const readingPaths = new Set(readingOrder.map((item) => item.href));\n const navigationItem = [...manifestItems.values()].find((item) =>\n item.properties.includes('nav'),\n );\n const tableOfContents = navigationItem\n ? await parseNavigationDocument(archive, navigationItem.href, signal)\n : [];\n\n return {\n direction,\n fixedLayout,\n language,\n packagePath,\n readingOrder,\n resources: [...manifestItems.values()].filter((item) => !readingPaths.has(item.href)),\n tableOfContents,\n title,\n };\n}\n\nasync function parseNavigationDocument(\n archive: EpubArchive,\n navigationPath: string,\n signal: AbortSignal,\n): Promise<{ href: string; title: string }[]> {\n const document = parseXml(await archive.readText(navigationPath, signal), 'EPUB navigation');\n const navigation =\n elementsByLocalName(document, 'nav').find((element) =>\n (element.getAttribute('epub:type') ?? element.getAttribute('type') ?? '')\n .split(/\\s+/)\n .includes('toc'),\n ) ?? elementsByLocalName(document, 'nav')[0];\n if (!navigation) return [];\n return elementsByLocalName(navigation, 'a').flatMap((anchor) => {\n const href = anchor.getAttribute('href');\n const title = anchor.textContent.trim();\n if (!href || !title) return [];\n const [resourceReference, fragment] = href.split('#', 2);\n const resourcePath = resolveArchivePath(navigationPath, resourceReference ?? '');\n return [{ href: fragment ? `${resourcePath}#${fragment}` : resourcePath, title }];\n });\n}\n\nfunction parseXml(source: string, label: string): XMLDocument {\n const document = new DOMParser().parseFromString(source, 'application/xml');\n if (elementsByLocalName(document, 'parsererror').length > 0) {\n throw new ReaderDocumentError('invalid-document', `Invalid ${label}.`);\n }\n return document;\n}\n\nfunction elementsByLocalName(root: Document | Element, localName: string): Element[] {\n return Array.from(root.getElementsByTagName('*')).filter(\n (element) => element.localName === localName,\n );\n}\n\nfunction elementText(root: Document | Element, localName: string): string | undefined {\n const [element] = elementsByLocalName(root, localName);\n const value = element?.textContent.trim();\n return value === '' ? undefined : value;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/reader/epub/path.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAYzD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAO9E;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE9D"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export function normalizeArchivePath(path) {
|
|
2
|
+
const segments = [];
|
|
3
|
+
for (const segment of decodeURIComponent(path).replaceAll('\\', '/').split('/')) {
|
|
4
|
+
if (segment === '' || segment === '.')
|
|
5
|
+
continue;
|
|
6
|
+
if (segment === '..') {
|
|
7
|
+
if (segments.length === 0)
|
|
8
|
+
throw new Error(`EPUB resource escapes the archive: ${path}`);
|
|
9
|
+
segments.pop();
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
segments.push(segment);
|
|
13
|
+
}
|
|
14
|
+
return segments.join('/');
|
|
15
|
+
}
|
|
16
|
+
export function resolveArchivePath(basePath, reference) {
|
|
17
|
+
const cleanReference = reference.split('#', 1)[0]?.split('?', 1)[0] ?? '';
|
|
18
|
+
if (cleanReference.startsWith('/'))
|
|
19
|
+
return normalizeArchivePath(cleanReference);
|
|
20
|
+
const baseDirectory = basePath.includes('/')
|
|
21
|
+
? basePath.slice(0, basePath.lastIndexOf('/') + 1)
|
|
22
|
+
: '';
|
|
23
|
+
return normalizeArchivePath(`${baseDirectory}${cleanReference}`);
|
|
24
|
+
}
|
|
25
|
+
export function isExternalReference(reference) {
|
|
26
|
+
return /^(?:https?:|mailto:|tel:|data:|javascript:|\/\/)/i.test(reference.trim());
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../../src/reader/epub/path.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,OAAO,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAChF,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG;YAAE,SAAS;QAChD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,IAAI,EAAE,CAAC,CAAC;YACzF,QAAQ,CAAC,GAAG,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,SAAiB;IACpE,MAAM,cAAc,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC1E,IAAI,cAAc,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,oBAAoB,CAAC,cAAc,CAAC,CAAC;IAChF,MAAM,aAAa,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAC1C,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClD,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,oBAAoB,CAAC,GAAG,aAAa,GAAG,cAAc,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,mDAAmD,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;AACpF,CAAC","sourcesContent":["export function normalizeArchivePath(path: string): string {\n const segments: string[] = [];\n for (const segment of decodeURIComponent(path).replaceAll('\\\\', '/').split('/')) {\n if (segment === '' || segment === '.') continue;\n if (segment === '..') {\n if (segments.length === 0) throw new Error(`EPUB resource escapes the archive: ${path}`);\n segments.pop();\n continue;\n }\n segments.push(segment);\n }\n return segments.join('/');\n}\n\nexport function resolveArchivePath(basePath: string, reference: string): string {\n const cleanReference = reference.split('#', 1)[0]?.split('?', 1)[0] ?? '';\n if (cleanReference.startsWith('/')) return normalizeArchivePath(cleanReference);\n const baseDirectory = basePath.includes('/')\n ? basePath.slice(0, basePath.lastIndexOf('/') + 1)\n : '';\n return normalizeArchivePath(`${baseDirectory}${cleanReference}`);\n}\n\nexport function isExternalReference(reference: string): boolean {\n return /^(?:https?:|mailto:|tel:|data:|javascript:|\\/\\/)/i.test(reference.trim());\n}\n"]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function sanitizeEpubDocument(source: string, resourcePath: string, resolveResourceUrl: (path: string) => string | undefined): string;
|
|
2
|
+
export declare function sanitizeEpubStylesheet(source: string, resourcePath: string, resolveResourceUrl: (path: string) => string | undefined): string;
|
|
3
|
+
//# sourceMappingURL=sanitizeEpubDocument.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeEpubDocument.d.ts","sourceRoot":"","sources":["../../../src/reader/epub/sanitizeEpubDocument.ts"],"names":[],"mappings":"AAKA,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACvD,MAAM,CAkBR;AAuDD,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACvD,MAAM,CAaR"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isExternalReference, resolveArchivePath } from './path';
|
|
2
|
+
const REMOVED_ELEMENTS = 'script, form, iframe, frame, object, embed';
|
|
3
|
+
const RESOURCE_ATTRIBUTES = ['src', 'poster', 'data'];
|
|
4
|
+
export function sanitizeEpubDocument(source, resourcePath, resolveResourceUrl) {
|
|
5
|
+
const document = new DOMParser().parseFromString(source, 'application/xhtml+xml');
|
|
6
|
+
if (document.querySelector('parsererror')) {
|
|
7
|
+
throw new Error(`Invalid EPUB XHTML resource: ${resourcePath}`);
|
|
8
|
+
}
|
|
9
|
+
document.querySelectorAll(REMOVED_ELEMENTS).forEach((element) => element.remove());
|
|
10
|
+
document
|
|
11
|
+
.querySelectorAll('meta[http-equiv]')
|
|
12
|
+
.forEach((element) => element.getAttribute('http-equiv')?.toLowerCase() === 'refresh' && element.remove());
|
|
13
|
+
sanitizeElementAttributes(document, resourcePath, resolveResourceUrl);
|
|
14
|
+
rewriteDocumentResources(document, resourcePath, resolveResourceUrl);
|
|
15
|
+
rewriteDocumentLinks(document);
|
|
16
|
+
return new XMLSerializer().serializeToString(document);
|
|
17
|
+
}
|
|
18
|
+
function sanitizeElementAttributes(document, resourcePath, resolveResourceUrl) {
|
|
19
|
+
for (const element of document.querySelectorAll('*')) {
|
|
20
|
+
for (const attribute of Array.from(element.attributes)) {
|
|
21
|
+
if (attribute.name.toLowerCase().startsWith('on'))
|
|
22
|
+
element.removeAttribute(attribute.name);
|
|
23
|
+
}
|
|
24
|
+
const inlineStyle = element.getAttribute('style');
|
|
25
|
+
if (inlineStyle) {
|
|
26
|
+
element.setAttribute('style', sanitizeEpubStylesheet(inlineStyle, resourcePath, resolveResourceUrl));
|
|
27
|
+
}
|
|
28
|
+
element.removeAttribute('srcset');
|
|
29
|
+
}
|
|
30
|
+
for (const style of document.querySelectorAll('style')) {
|
|
31
|
+
style.textContent = sanitizeEpubStylesheet(style.textContent, resourcePath, resolveResourceUrl);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function rewriteDocumentResources(document, resourcePath, resolveResourceUrl) {
|
|
35
|
+
for (const attributeName of RESOURCE_ATTRIBUTES) {
|
|
36
|
+
for (const element of document.querySelectorAll(`[${attributeName}]`)) {
|
|
37
|
+
rewriteResourceAttribute(element, attributeName, resourcePath, resolveResourceUrl);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
for (const link of document.querySelectorAll('link[href]')) {
|
|
41
|
+
rewriteResourceAttribute(link, 'href', resourcePath, resolveResourceUrl);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function rewriteDocumentLinks(document) {
|
|
45
|
+
for (const anchor of document.querySelectorAll('a[href]')) {
|
|
46
|
+
const href = anchor.getAttribute('href')?.trim();
|
|
47
|
+
if (!href)
|
|
48
|
+
continue;
|
|
49
|
+
if (/^(?:https?:|mailto:|tel:|\/\/)/i.test(href)) {
|
|
50
|
+
anchor.setAttribute('data-reader-external-href', href);
|
|
51
|
+
anchor.removeAttribute('href');
|
|
52
|
+
}
|
|
53
|
+
else if (isExternalReference(href)) {
|
|
54
|
+
anchor.removeAttribute('href');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function sanitizeEpubStylesheet(source, resourcePath, resolveResourceUrl) {
|
|
59
|
+
const withoutImports = source.replaceAll(/@import\s+(?:url\([^)]*\)|[^;]+);?/gi, '');
|
|
60
|
+
return withoutImports.replaceAll(/url\(\s*(['"]?)([^)'"\s]+)\1\s*\)/gi, (match, _quote, value) => {
|
|
61
|
+
if (isExternalReference(value))
|
|
62
|
+
return 'url(about:blank)';
|
|
63
|
+
try {
|
|
64
|
+
return `url("${resolveResourceUrl(resolveArchivePath(resourcePath, value)) ?? 'about:blank'}")`;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return 'url(about:blank)';
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function rewriteResourceAttribute(element, attributeName, resourcePath, resolveResourceUrl) {
|
|
72
|
+
const value = element.getAttribute(attributeName)?.trim();
|
|
73
|
+
if (!value)
|
|
74
|
+
return;
|
|
75
|
+
if (isExternalReference(value)) {
|
|
76
|
+
element.removeAttribute(attributeName);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const resolved = resolveResourceUrl(resolveArchivePath(resourcePath, value));
|
|
81
|
+
if (resolved === undefined)
|
|
82
|
+
element.removeAttribute(attributeName);
|
|
83
|
+
else
|
|
84
|
+
element.setAttribute(attributeName, resolved);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
element.removeAttribute(attributeName);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=sanitizeEpubDocument.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sanitizeEpubDocument.js","sourceRoot":"","sources":["../../../src/reader/epub/sanitizeEpubDocument.ts"],"names":[],"mappings":"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","sourcesContent":["import { isExternalReference, resolveArchivePath } from './path';\n\nconst REMOVED_ELEMENTS = 'script, form, iframe, frame, object, embed';\nconst RESOURCE_ATTRIBUTES = ['src', 'poster', 'data'] as const;\n\nexport function sanitizeEpubDocument(\n source: string,\n resourcePath: string,\n resolveResourceUrl: (path: string) => string | undefined,\n): string {\n const document = new DOMParser().parseFromString(source, 'application/xhtml+xml');\n if (document.querySelector('parsererror')) {\n throw new Error(`Invalid EPUB XHTML resource: ${resourcePath}`);\n }\n\n document.querySelectorAll(REMOVED_ELEMENTS).forEach((element) => element.remove());\n document\n .querySelectorAll('meta[http-equiv]')\n .forEach(\n (element) =>\n element.getAttribute('http-equiv')?.toLowerCase() === 'refresh' && element.remove(),\n );\n\n sanitizeElementAttributes(document, resourcePath, resolveResourceUrl);\n rewriteDocumentResources(document, resourcePath, resolveResourceUrl);\n rewriteDocumentLinks(document);\n return new XMLSerializer().serializeToString(document);\n}\n\nfunction sanitizeElementAttributes(\n document: XMLDocument,\n resourcePath: string,\n resolveResourceUrl: (path: string) => string | undefined,\n): void {\n for (const element of document.querySelectorAll('*')) {\n for (const attribute of Array.from(element.attributes)) {\n if (attribute.name.toLowerCase().startsWith('on')) element.removeAttribute(attribute.name);\n }\n const inlineStyle = element.getAttribute('style');\n if (inlineStyle) {\n element.setAttribute(\n 'style',\n sanitizeEpubStylesheet(inlineStyle, resourcePath, resolveResourceUrl),\n );\n }\n element.removeAttribute('srcset');\n }\n\n for (const style of document.querySelectorAll('style')) {\n style.textContent = sanitizeEpubStylesheet(style.textContent, resourcePath, resolveResourceUrl);\n }\n}\n\nfunction rewriteDocumentResources(\n document: XMLDocument,\n resourcePath: string,\n resolveResourceUrl: (path: string) => string | undefined,\n): void {\n for (const attributeName of RESOURCE_ATTRIBUTES) {\n for (const element of document.querySelectorAll(`[${attributeName}]`)) {\n rewriteResourceAttribute(element, attributeName, resourcePath, resolveResourceUrl);\n }\n }\n\n for (const link of document.querySelectorAll('link[href]')) {\n rewriteResourceAttribute(link, 'href', resourcePath, resolveResourceUrl);\n }\n}\n\nfunction rewriteDocumentLinks(document: XMLDocument): void {\n for (const anchor of document.querySelectorAll('a[href]')) {\n const href = anchor.getAttribute('href')?.trim();\n if (!href) continue;\n if (/^(?:https?:|mailto:|tel:|\\/\\/)/i.test(href)) {\n anchor.setAttribute('data-reader-external-href', href);\n anchor.removeAttribute('href');\n } else if (isExternalReference(href)) {\n anchor.removeAttribute('href');\n }\n }\n}\n\nexport function sanitizeEpubStylesheet(\n source: string,\n resourcePath: string,\n resolveResourceUrl: (path: string) => string | undefined,\n): string {\n const withoutImports = source.replaceAll(/@import\\s+(?:url\\([^)]*\\)|[^;]+);?/gi, '');\n return withoutImports.replaceAll(\n /url\\(\\s*(['\"]?)([^)'\"\\s]+)\\1\\s*\\)/gi,\n (match, _quote, value: string) => {\n if (isExternalReference(value)) return 'url(about:blank)';\n try {\n return `url(\"${resolveResourceUrl(resolveArchivePath(resourcePath, value)) ?? 'about:blank'}\")`;\n } catch {\n return 'url(about:blank)';\n }\n },\n );\n}\n\nfunction rewriteResourceAttribute(\n element: Element,\n attributeName: string,\n resourcePath: string,\n resolveResourceUrl: (path: string) => string | undefined,\n): void {\n const value = element.getAttribute(attributeName)?.trim();\n if (!value) return;\n if (isExternalReference(value)) {\n element.removeAttribute(attributeName);\n return;\n }\n try {\n const resolved = resolveResourceUrl(resolveArchivePath(resourcePath, value));\n if (resolved === undefined) element.removeAttribute(attributeName);\n else element.setAttribute(attributeName, resolved);\n } catch {\n element.removeAttribute(attributeName);\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadReaderSourceAsync.d.ts","sourceRoot":"","sources":["../../src/reader/loadReaderSourceAsync.ts"],"names":[],"mappings":"AAEA,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,UAAU,CAAC,CA8CrB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const MAX_READER_SOURCE_BYTES = 256 * 1024 * 1024;
|
|
2
|
+
export async function loadReaderSourceAsync(sourceUri, signal) {
|
|
3
|
+
const response = await fetch(sourceUri, {
|
|
4
|
+
credentials: 'omit',
|
|
5
|
+
signal,
|
|
6
|
+
});
|
|
7
|
+
if (!response.ok) {
|
|
8
|
+
throw new Error(`Reader source request failed with HTTP ${response.status}.`);
|
|
9
|
+
}
|
|
10
|
+
const declaredLength = Number(response.headers.get('content-length'));
|
|
11
|
+
if (Number.isFinite(declaredLength) && declaredLength > MAX_READER_SOURCE_BYTES) {
|
|
12
|
+
throw new Error(`Reader source exceeds the ${MAX_READER_SOURCE_BYTES}-byte download limit.`);
|
|
13
|
+
}
|
|
14
|
+
if (response.body === null) {
|
|
15
|
+
return validateSourceSize(new Uint8Array(await response.arrayBuffer()));
|
|
16
|
+
}
|
|
17
|
+
const reader = response.body.getReader();
|
|
18
|
+
const chunks = [];
|
|
19
|
+
let byteLength = 0;
|
|
20
|
+
try {
|
|
21
|
+
for (;;) {
|
|
22
|
+
const result = await reader.read();
|
|
23
|
+
if (result.done)
|
|
24
|
+
break;
|
|
25
|
+
byteLength += result.value.byteLength;
|
|
26
|
+
if (byteLength > MAX_READER_SOURCE_BYTES) {
|
|
27
|
+
throw new Error(`Reader source exceeds the ${MAX_READER_SOURCE_BYTES}-byte download limit.`);
|
|
28
|
+
}
|
|
29
|
+
chunks.push(result.value);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
finally {
|
|
33
|
+
reader.releaseLock();
|
|
34
|
+
}
|
|
35
|
+
const bytes = new Uint8Array(byteLength);
|
|
36
|
+
let offset = 0;
|
|
37
|
+
for (const chunk of chunks) {
|
|
38
|
+
bytes.set(chunk, offset);
|
|
39
|
+
offset += chunk.byteLength;
|
|
40
|
+
}
|
|
41
|
+
return bytes;
|
|
42
|
+
}
|
|
43
|
+
function validateSourceSize(bytes) {
|
|
44
|
+
if (bytes.byteLength > MAX_READER_SOURCE_BYTES) {
|
|
45
|
+
throw new Error(`Reader source exceeds the ${MAX_READER_SOURCE_BYTES}-byte download limit.`);
|
|
46
|
+
}
|
|
47
|
+
return bytes;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=loadReaderSourceAsync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadReaderSourceAsync.js","sourceRoot":"","sources":["../../src/reader/loadReaderSourceAsync.ts"],"names":[],"mappings":"AAAA,MAAM,uBAAuB,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,SAAiB,EACjB,MAAmB;IAEnB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;QACtC,WAAW,EAAE,MAAM;QACnB,MAAM;KACP,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0CAA0C,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACtE,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,uBAAuB,EAAE,CAAC;QAChF,MAAM,IAAI,KAAK,CAAC,6BAA6B,uBAAuB,uBAAuB,CAAC,CAAC;IAC/F,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC3B,OAAO,kBAAkB,CAAC,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;IACzC,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,CAAC;QACH,SAAS,CAAC;YACR,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACnC,IAAI,MAAM,CAAC,IAAI;gBAAE,MAAM;YACvB,UAAU,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;YACtC,IAAI,UAAU,GAAG,uBAAuB,EAAE,CAAC;gBACzC,MAAM,IAAI,KAAK,CACb,6BAA6B,uBAAuB,uBAAuB,CAC5E,CAAC;YACJ,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,WAAW,EAAE,CAAC;IACvB,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IACzC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAiB;IAC3C,IAAI,KAAK,CAAC,UAAU,GAAG,uBAAuB,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,6BAA6B,uBAAuB,uBAAuB,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["const MAX_READER_SOURCE_BYTES = 256 * 1024 * 1024;\n\nexport async function loadReaderSourceAsync(\n sourceUri: string,\n signal: AbortSignal,\n): Promise<Uint8Array> {\n const response = await fetch(sourceUri, {\n credentials: 'omit',\n signal,\n });\n\n if (!response.ok) {\n throw new Error(`Reader source request failed with HTTP ${response.status}.`);\n }\n\n const declaredLength = Number(response.headers.get('content-length'));\n if (Number.isFinite(declaredLength) && declaredLength > MAX_READER_SOURCE_BYTES) {\n throw new Error(`Reader source exceeds the ${MAX_READER_SOURCE_BYTES}-byte download limit.`);\n }\n\n if (response.body === null) {\n return validateSourceSize(new Uint8Array(await response.arrayBuffer()));\n }\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let byteLength = 0;\n\n try {\n for (;;) {\n const result = await reader.read();\n if (result.done) break;\n byteLength += result.value.byteLength;\n if (byteLength > MAX_READER_SOURCE_BYTES) {\n throw new Error(\n `Reader source exceeds the ${MAX_READER_SOURCE_BYTES}-byte download limit.`,\n );\n }\n chunks.push(result.value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const bytes = new Uint8Array(byteLength);\n let offset = 0;\n for (const chunk of chunks) {\n bytes.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return bytes;\n}\n\nfunction validateSourceSize(bytes: Uint8Array): Uint8Array {\n if (bytes.byteLength > MAX_READER_SOURCE_BYTES) {\n throw new Error(`Reader source exceeds the ${MAX_READER_SOURCE_BYTES}-byte download limit.`);\n }\n return bytes;\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ReaderViewportState } from './types';
|
|
2
|
+
export declare const LOADING_STATE: ReaderViewportState;
|
|
3
|
+
export declare const READER_STYLES = "\n :root, body, #root { height: 100%; margin: 0; overflow: hidden; }\n * { box-sizing: border-box; }\n .reader-root { background: #fff; color: #111; height: 100%; outline: none; overflow: hidden; touch-action: pan-y; width: 100%; }\n .reader-dark { background: #111; color: #f5f5f5; }\n .reader-sepia { background: #f4ecd8; color: #3f3527; }\n .reader-content { height: 100%; overflow: auto; position: relative; width: 100%; }\n .reader-content > div:not(.reader-pdf-page) { height: 100%; width: 100%; }\n .reader-pdf-page { --user-unit: 1; --total-scale-factor: calc(var(--scale-factor) * var(--user-unit)); margin: 0 auto; position: relative; }\n .reader-pdf-page canvas { display: block; position: absolute; inset: 0; }\n .reader-pdf-text-layer { --min-font-size: 1; --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size)); --min-font-size-inv: calc(1 / var(--min-font-size)); inset: 0; line-height: 1; opacity: 1; overflow: hidden; position: absolute; text-align: initial; text-size-adjust: none; transform-origin: 0 0; }\n .reader-pdf-text-layer span, .reader-pdf-text-layer br { color: transparent; cursor: text; position: absolute; transform-origin: 0 0; user-select: text; white-space: pre; }\n .reader-pdf-text-layer > :not(.markedContent), .reader-pdf-text-layer .markedContent span:not(.markedContent) { --font-height: 0; --scale-x: 1; --rotate: 0deg; font-size: calc(var(--text-scale-factor) * var(--font-height)); transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv)); }\n .reader-pdf-text-layer .markedContent { display: contents; }\n .reader-pdf-text-layer ::selection { background: rgba(0, 100, 255, 0.35); }\n .reader-pdf-link-layer { inset: 0; pointer-events: none; position: absolute; }\n .reader-pdf-link-layer a { outline-offset: 2px; pointer-events: auto; position: absolute; }\n";
|
|
4
|
+
//# sourceMappingURL=readerViewportPresentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readerViewportPresentation.d.ts","sourceRoot":"","sources":["../../src/reader/readerViewportPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD,eAAO,MAAM,aAAa,EAAE,mBAM3B,CAAC;AAEF,eAAO,MAAM,aAAa,o1DAiBzB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const LOADING_STATE = {
|
|
2
|
+
canGoNext: false,
|
|
3
|
+
canGoPrevious: false,
|
|
4
|
+
page: 1,
|
|
5
|
+
progress: 0,
|
|
6
|
+
status: 'loading',
|
|
7
|
+
};
|
|
8
|
+
export const READER_STYLES = `
|
|
9
|
+
:root, body, #root { height: 100%; margin: 0; overflow: hidden; }
|
|
10
|
+
* { box-sizing: border-box; }
|
|
11
|
+
.reader-root { background: #fff; color: #111; height: 100%; outline: none; overflow: hidden; touch-action: pan-y; width: 100%; }
|
|
12
|
+
.reader-dark { background: #111; color: #f5f5f5; }
|
|
13
|
+
.reader-sepia { background: #f4ecd8; color: #3f3527; }
|
|
14
|
+
.reader-content { height: 100%; overflow: auto; position: relative; width: 100%; }
|
|
15
|
+
.reader-content > div:not(.reader-pdf-page) { height: 100%; width: 100%; }
|
|
16
|
+
.reader-pdf-page { --user-unit: 1; --total-scale-factor: calc(var(--scale-factor) * var(--user-unit)); margin: 0 auto; position: relative; }
|
|
17
|
+
.reader-pdf-page canvas { display: block; position: absolute; inset: 0; }
|
|
18
|
+
.reader-pdf-text-layer { --min-font-size: 1; --text-scale-factor: calc(var(--total-scale-factor) * var(--min-font-size)); --min-font-size-inv: calc(1 / var(--min-font-size)); inset: 0; line-height: 1; opacity: 1; overflow: hidden; position: absolute; text-align: initial; text-size-adjust: none; transform-origin: 0 0; }
|
|
19
|
+
.reader-pdf-text-layer span, .reader-pdf-text-layer br { color: transparent; cursor: text; position: absolute; transform-origin: 0 0; user-select: text; white-space: pre; }
|
|
20
|
+
.reader-pdf-text-layer > :not(.markedContent), .reader-pdf-text-layer .markedContent span:not(.markedContent) { --font-height: 0; --scale-x: 1; --rotate: 0deg; font-size: calc(var(--text-scale-factor) * var(--font-height)); transform: rotate(var(--rotate)) scaleX(var(--scale-x)) scale(var(--min-font-size-inv)); }
|
|
21
|
+
.reader-pdf-text-layer .markedContent { display: contents; }
|
|
22
|
+
.reader-pdf-text-layer ::selection { background: rgba(0, 100, 255, 0.35); }
|
|
23
|
+
.reader-pdf-link-layer { inset: 0; pointer-events: none; position: absolute; }
|
|
24
|
+
.reader-pdf-link-layer a { outline-offset: 2px; pointer-events: auto; position: absolute; }
|
|
25
|
+
`;
|
|
26
|
+
//# sourceMappingURL=readerViewportPresentation.js.map
|