import type * as PdfJs from 'pdfjs-dist' import { pdfText, type PdfItem } from './docs.ts' // A PDF's text, read in the view with pdf.js. The view's frame allows no worker, no eval, and no // network, so pdf.js runs in this thread (its worker's handler set on globalThis, which it looks for // before it would start a worker), with font loading off, from the bytes it is handed; pdf.js 6 has // no eval. Both // halves load on the first PDF: they are most of this plugin's size, and the plugin's definitions, // which are this same code evaluated without a page, never reach them. let loading: Promise | null = null function pdfjs(): Promise { loading ??= (async () => { ;(globalThis as { pdfjsWorker?: unknown }).pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.min.mjs') return await import('pdfjs-dist/build/pdf.min.mjs') })() return loading } export async function readPdf(bytes: Uint8Array, name: string): Promise { const lib = await pdfjs() const task = lib.getDocument({ data: bytes, disableFontFace: true }) try { const doc = await task.promise.catch((err: unknown) => { if ((err as { name?: string }).name === 'PasswordException') throw new Error(`${name} is password-protected.`) throw err }) const pages: PdfItem[][] = [] for (let n = 1; n <= doc.numPages; n++) { const page = await doc.getPage(n) pages.push((await page.getTextContent()).items as PdfItem[]) page.cleanup() } const text = pdfText(pages) if (!text) throw new Error(`${name} has no text layer, as a scan has none; scans are not read yet.`) return text } finally { await task.destroy() } }