vomebook commited on
Commit
44759b1
·
1 Parent(s): 61e3a76

perf(reader): create PDF page canvases on first render

Browse files

Every PDF page shell created a default 300x150 canvas at mount. A 234-page
original PDF therefore held 234 blank backing stores (~16.7M pixels, ~67 MB)
before any page was visible, and the count scaled with the document.

Create the canvas in the renderer instead, matching the v3 path. Measured on a
production 234-page PDF: 234 canvases / 16.7M px at open -> 0 / 0, with the same
pages rendered as they scroll into view.

static/reader/pdf/reader-pdf-page-window.mjs CHANGED
@@ -632,13 +632,14 @@ export function createReaderPdfPageWindow(readerContext) {
632
  shell.tabIndex = 0;
633
  shell.setAttribute("role", "region");
634
  shell.setAttribute("aria-label", `第 ${page} 页`);
635
- const canvas = document.createElement("canvas");
636
- canvas.setAttribute("aria-hidden", "true");
 
637
  const textLayer = document.createElement("div");
638
  textLayer.className = "reader-pdf-text";
639
  textLayer.setAttribute("role", "document");
640
  textLayer.setAttribute("aria-label", `第 ${page} 页正文`);
641
- shell.append(canvas, textLayer);
642
  const pageState = document.createElement("div");
643
  pageState.className = "reader-page-state";
644
  readerContext.setPdfPageLoadingState(pageState, page);
 
632
  shell.tabIndex = 0;
633
  shell.setAttribute("role", "region");
634
  shell.setAttribute("aria-label", `第 ${page} 页`);
635
+ // The canvas is created by the renderer on first use: a 300x150 default
636
+ // canvas per shell costs ~180 KB, so a 250-page PDF held ~45 MB of blank
637
+ // backing stores before any page was visible.
638
  const textLayer = document.createElement("div");
639
  textLayer.className = "reader-pdf-text";
640
  textLayer.setAttribute("role", "document");
641
  textLayer.setAttribute("aria-label", `第 ${page} 页正文`);
642
+ shell.append(textLayer);
643
  const pageState = document.createElement("div");
644
  pageState.className = "reader-page-state";
645
  readerContext.setPdfPageLoadingState(pageState, page);
static/reader/pdf/reader-pdf-renderer.mjs CHANGED
@@ -546,6 +546,7 @@ export function createReaderPdfRenderer(readerContext) {
546
  if (renderController.signal.aborted) throw readerContext.readerAbortError();
547
  const pdf = readerContext.pdfDocument;
548
  if (!pdf) throw readerContext.readerAbortError();
 
549
  do {
550
  delete shell.dataset.pendingRerender;
551
  const generation = readerContext.readerRuntime.currentGeneration("pdf"),
@@ -557,7 +558,12 @@ export function createReaderPdfRenderer(readerContext) {
557
  scale = Math.min(3, shell.clientWidth / base.width),
558
  viewport = page.getViewport({ scale }),
559
  outputScale = Math.min(2, Math.max(1, Number(window.devicePixelRatio) || 1));
560
- const canvas = shell.querySelector("canvas");
 
 
 
 
 
561
  canvas.width = Math.ceil(viewport.width * outputScale);
562
  canvas.height = Math.ceil(viewport.height * outputScale);
563
  canvas.style.width = "100%";
@@ -584,7 +590,7 @@ export function createReaderPdfRenderer(readerContext) {
584
  if (renderController.signal.aborted) throw readerContext.readerAbortError();
585
  if (!readerContext.isReaderGenerationCurrent("pdf", generation)) shell.dataset.pendingRerender = "1";
586
  } while (shell.dataset.pendingRerender);
587
- shell.querySelector("canvas").classList.add("ready");
588
  const pageState = shell.querySelector(".reader-page-state");
589
  if (pageState) pageState.hidden = true;
590
  shell.dataset.renderState = "rendered";
 
546
  if (renderController.signal.aborted) throw readerContext.readerAbortError();
547
  const pdf = readerContext.pdfDocument;
548
  if (!pdf) throw readerContext.readerAbortError();
549
+ let canvas = null;
550
  do {
551
  delete shell.dataset.pendingRerender;
552
  const generation = readerContext.readerRuntime.currentGeneration("pdf"),
 
558
  scale = Math.min(3, shell.clientWidth / base.width),
559
  viewport = page.getViewport({ scale }),
560
  outputScale = Math.min(2, Math.max(1, Number(window.devicePixelRatio) || 1));
561
+ canvas = shell.querySelector("canvas");
562
+ if (!canvas) {
563
+ canvas = document.createElement("canvas");
564
+ canvas.setAttribute("aria-hidden", "true");
565
+ shell.prepend(canvas);
566
+ }
567
  canvas.width = Math.ceil(viewport.width * outputScale);
568
  canvas.height = Math.ceil(viewport.height * outputScale);
569
  canvas.style.width = "100%";
 
590
  if (renderController.signal.aborted) throw readerContext.readerAbortError();
591
  if (!readerContext.isReaderGenerationCurrent("pdf", generation)) shell.dataset.pendingRerender = "1";
592
  } while (shell.dataset.pendingRerender);
593
+ canvas.classList.add("ready");
594
  const pageState = shell.querySelector(".reader-page-state");
595
  if (pageState) pageState.hidden = true;
596
  shell.dataset.renderState = "rendered";