import { BlobWriter, TextReader, Uint8ArrayReader, ZipWriter, } from "@zip.js/zip.js"; export async function createEpubFixture( options: { brokenLink?: boolean; activeContent?: boolean; compressedMimetype?: boolean; svgTitlePage?: boolean; svgImageHref?: string; repeatedImages?: number; packagedStyles?: boolean; obfuscatedFont?: boolean; secondChapter?: boolean; direction?: "ltr" | "rtl"; fixedLayout?: boolean; } = {}, ): Promise { const packagedStyles = options.packagedStyles || options.obfuscatedFont; const writer = new ZipWriter(new BlobWriter("application/epub+zip")); await writer.add("mimetype", new TextReader("application/epub+zip"), { level: options.compressedMimetype ? 6 : 0, }); await writer.add( "META-INF/container.xml", new TextReader( ``, ), ); if (options.obfuscatedFont) await writer.add( "META-INF/encryption.xml", new TextReader( ``, ), ); await writer.add( "EPUB/package.opf", new TextReader( `urn:test:bookFixture BookenAda Example${options.fixedLayout ? 'pre-paginatedportraitnone' : ""}${options.secondChapter ? '' : ""}${packagedStyles ? '' : ""}${options.svgTitlePage ? '' : ""}${options.svgTitlePage ? '' : ""}${options.secondChapter ? '' : ""}`, ), ); await writer.add( "EPUB/nav.xhtml", new TextReader( `Contents`, ), ); await writer.add( "EPUB/chapter.xhtml", new TextReader( `Opening chapter${packagedStyles ? '' : ""}

Hello

Local reading and searchable phrase.

${options.activeContent ? '
' : ""}${Array.from({ length: options.repeatedImages ?? 1 }, () => '').join("")}Next`, ), ); if (options.secondChapter) await writer.add( "EPUB/chapter-two.xhtml", new TextReader( `Second chapter

Answer

The searchable phrase appears twice: searchable phrase.

`, ), ); if (packagedStyles) { await writer.add( "EPUB/styles/book.css", new TextReader( `@font-face{font-family:Fixture;src:url("../fonts/book.woff2")}body{font-family:Fixture;background-image:url("https://example.invalid/tracker.png")}h1{color:rebeccapurple}`, ), ); const fontBytes = new Uint8Array([0x77, 0x4f, 0x46, 0x32, 0, 0, 0, 0]); if (options.obfuscatedFont) { const key = new Uint8Array( await crypto.subtle.digest( "SHA-1", new TextEncoder().encode("urn:test:book"), ), ); for (let index = 0; index < fontBytes.length; index += 1) fontBytes[index] = fontBytes[index]! ^ key[index % key.length]!; } await writer.add("EPUB/fonts/book.woff2", new Uint8ArrayReader(fontBytes)); } if (options.svgTitlePage) await writer.add( "EPUB/title.svg", new TextReader( ``, ), ); await writer.add( "EPUB/cover.png", new Uint8ArrayReader( new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]), ), ); const blob = await writer.close(); return new File([blob], "fixture.epub", { type: "application/epub+zip" }); }