app-ci-deploy / test-build-deploy (push) Has been cancelled
Rendering a 36-page AQA exam PDF sequentially created 36 large canvas elements, causing memory pressure and keeping pdfStatus='loading' for 60–120s in headless Chrome. Two changes: 1. pdfLoader.ts: reuse a single canvas (reduces peak memory from ~120MB to ~4MB) and fire onPageReady callback after each page so callers can stream pages to the canvas as they render. 2. ExamTemplateSetupPage.tsx: use the callback to add each PDF page shape to the tldraw canvas the moment it renders, making the first page visible within a few seconds rather than after all pages load. Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import * as pdfjsLib from "pdfjs-dist"
|
|
import pdfWorkerSrc from "pdfjs-dist/build/pdf.worker.mjs?url"
|
|
|
|
import { PAGE_WIDTH } from "../../../utils/exam-canvas/model"
|
|
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerSrc
|
|
|
|
export interface PdfPageImage {
|
|
pageNumber: number
|
|
src: string
|
|
width: number
|
|
height: number
|
|
}
|
|
|
|
export async function loadPdfPageImages(
|
|
pdfBytes: ArrayBuffer,
|
|
targetWidth = PAGE_WIDTH,
|
|
onPageReady?: (pages: PdfPageImage[]) => void,
|
|
): Promise<PdfPageImage[]> {
|
|
const pdf = await pdfjsLib.getDocument({ data: new Uint8Array(pdfBytes) }).promise
|
|
const pages: PdfPageImage[] = []
|
|
// Reuse a single canvas across all pages to avoid allocating ~120 MB of canvas memory
|
|
// for a typical 36-page exam paper.
|
|
const canvas = document.createElement("canvas")
|
|
|
|
for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber += 1) {
|
|
const page = await pdf.getPage(pageNumber)
|
|
const baseViewport = page.getViewport({ scale: 1 })
|
|
const scale = targetWidth / baseViewport.width
|
|
const viewport = page.getViewport({ scale })
|
|
canvas.width = Math.ceil(viewport.width)
|
|
canvas.height = Math.ceil(viewport.height)
|
|
const context = canvas.getContext("2d")
|
|
if (!context) throw new Error("Unable to create PDF render canvas")
|
|
context.clearRect(0, 0, canvas.width, canvas.height)
|
|
await page.render({ canvasContext: context, viewport }).promise
|
|
pages.push({
|
|
pageNumber,
|
|
src: canvas.toDataURL("image/png"),
|
|
width: canvas.width,
|
|
height: canvas.height,
|
|
})
|
|
onPageReady?.([...pages])
|
|
}
|
|
|
|
return pages
|
|
}
|