1import { downloadAttachmentForPreview } from "./attachmentDownload";
2
3export function isPdfPreviewUri(uri: string): boolean {
4 try {
5 return /\.pdf$/i.test(new URL(uri).pathname);
6 } catch {
7 return false;
8 }
9}
10
11export function pdfInitialPage(uri: string): number {
12 const page = Number(new URLSearchParams(uri.split("#", 2)[1]).get("page"));
13 return Number.isSafeInteger(page) && page > 0 ? page : 1;
14}
15
16export function pdfLinkUrl(href: string, sourceUri: string): string | null {
17 if (href.trim().length === 0) return null;
18 try {
19 const url = new URL(href, sourceUri);
20 return url.protocol === "http:" || url.protocol === "https:" ? url.href : null;
21 } catch {
22 return null;
23 }
24}
25
26export async function preparePdfPreview(uri: string, signal: AbortSignal) {
27 if (signal.aborted) return null;
28 const source = uri.split("#", 1)[0]!;
29 const cached = /^(file|content):/.test(source)
30 ? { uri: source, dispose: () => {} }
31 : await downloadAttachmentForPreview({
32 url: source,
33 attachment: { name: "preview.pdf", mimeType: "application/pdf" },
34 signal,
35 });
36 if (cached === null) return null;
37 try {
38 const { File, FileMode } = await import("expo-file-system");
39 const handle = new File(cached.uri).open(FileMode.ReadOnly);
40 try {
41 const header = await handle.readBytes(5);
42 if (String.fromCharCode(...header) !== "%PDF-") {
43 throw new Error("The server did not return a PDF. Check the connection and try again.");
44 }
45 } finally {
46 handle.close();
47 }
48 if (signal.aborted) {
49 cached.dispose();
50 return null;
51 }
52 return cached;
53 } catch (error) {
54 cached.dispose();
55 if (signal.aborted) return null;
56 throw error;
57 }
58}