diff --git a/apps/readest-app/src/__tests__/document/document-loader-epub-case.test.ts b/apps/readest-app/src/__tests__/document/document-loader-epub-case.test.ts
new file mode 100644
index 00000000..bc4baf54
--- /dev/null
+++ b/apps/readest-app/src/__tests__/document/document-loader-epub-case.test.ts
@@ -0,0 +1,64 @@
+import { describe, expect, it } from 'vitest';
+
+import { DocumentLoader } from '@/libs/document';
+
+const createCaseMismatchEpub = async () => {
+ const { ZipWriter, BlobWriter, TextReader } = await import('@zip.js/zip.js');
+ const writer = new ZipWriter(new BlobWriter('application/epub+zip'));
+
+ await writer.add('mimetype', new TextReader('application/epub+zip'), { level: 0 });
+ await writer.add(
+ 'META-INF/container.xml',
+ new TextReader(`
+
Hello from the lowercase chapter entry.
+ +`), + ); + + const blob = await writer.close(); + return new File([blob], 'case-mismatch.epub', { type: 'application/epub+zip' }); +}; + +describe('DocumentLoader EPUB zip lookup', () => { + it('loads EPUB resources when manifest href casing differs from the zip entry', async () => { + const file = await createCaseMismatchEpub(); + const loader = new DocumentLoader(file); + + const { book, format } = await loader.open(); + expect(format).toBe('EPUB'); + + const doc = await book.sections[0]!.createDocument(); + expect(doc.body.textContent).toContain('Hello from the lowercase chapter entry.'); + }); +}); diff --git a/apps/readest-app/src/libs/document.ts b/apps/readest-app/src/libs/document.ts index 4111fdfc..0475e022 100644 --- a/apps/readest-app/src/libs/document.ts +++ b/apps/readest-app/src/libs/document.ts @@ -167,10 +167,23 @@ export class DocumentLoader { const reader = new ZipReader(new BlobReader(this.file)); const entries = await reader.getEntries(); const map = new Map(entries.map((entry) => [entry.filename, entry])); + const lowercaseMap = new Map