pdfjs-reader-core 0.5.11 → 0.5.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1926,8 +1926,10 @@ function getRenderConfig(quality = "auto", capabilities) {
1926
1926
  switch (quality) {
1927
1927
  case "low":
1928
1928
  return {
1929
- canvasScaleFactor: Math.min(1, caps.devicePixelRatio * 0.5),
1930
- maxCanvasDimension: 2048,
1929
+ canvasScaleFactor: caps.devicePixelRatio,
1930
+ // 3072² ≈ 9 MP — fits a full-DPR US Letter (DPR=3 → 1836×2376) at
1931
+ // default scale and caps at deep zoom on memory-constrained iPhones.
1932
+ maxCanvasDimension: 3072,
1931
1933
  overscanPages: 1,
1932
1934
  maxPagesInMemory: 3,
1933
1935
  scrollDebounceMs: 100,
@@ -1940,7 +1942,8 @@ function getRenderConfig(quality = "auto", capabilities) {
1940
1942
  };
1941
1943
  case "medium":
1942
1944
  return {
1943
- canvasScaleFactor: Math.min(1.5, caps.devicePixelRatio * 0.75),
1945
+ canvasScaleFactor: caps.devicePixelRatio,
1946
+ // 4096² ≈ 16 MP — within iOS Safari per-canvas budget across versions.
1944
1947
  maxCanvasDimension: 4096,
1945
1948
  overscanPages: 2,
1946
1949
  maxPagesInMemory: 5,
@@ -5227,9 +5230,37 @@ var init_ThumbnailPanel = __esm({
5227
5230
  onClick,
5228
5231
  scale
5229
5232
  }) {
5233
+ const buttonRef = (0, import_react18.useRef)(null);
5230
5234
  const canvasRef = (0, import_react18.useRef)(null);
5231
5235
  const [isRendered, setIsRendered] = (0, import_react18.useState)(false);
5236
+ const [shouldRender, setShouldRender] = (0, import_react18.useState)(false);
5237
+ (0, import_react18.useEffect)(() => {
5238
+ if (shouldRender) return;
5239
+ const el = buttonRef.current;
5240
+ if (!el) return;
5241
+ if (typeof IntersectionObserver === "undefined") {
5242
+ setShouldRender(true);
5243
+ return;
5244
+ }
5245
+ const observer = new IntersectionObserver(
5246
+ (entries) => {
5247
+ for (const entry of entries) {
5248
+ if (entry.isIntersecting) {
5249
+ setShouldRender(true);
5250
+ observer.disconnect();
5251
+ break;
5252
+ }
5253
+ }
5254
+ },
5255
+ // 200px root margin so the page is fetched just before it scrolls
5256
+ // into view, hiding the render latency.
5257
+ { rootMargin: "200px" }
5258
+ );
5259
+ observer.observe(el);
5260
+ return () => observer.disconnect();
5261
+ }, [shouldRender]);
5232
5262
  (0, import_react18.useEffect)(() => {
5263
+ if (!shouldRender) return;
5233
5264
  let cancelled = false;
5234
5265
  const renderThumbnail = async () => {
5235
5266
  const canvas = canvasRef.current;
@@ -5263,10 +5294,11 @@ var init_ThumbnailPanel = __esm({
5263
5294
  return () => {
5264
5295
  cancelled = true;
5265
5296
  };
5266
- }, [document2, pageNumber, scale]);
5297
+ }, [document2, pageNumber, scale, shouldRender]);
5267
5298
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
5268
5299
  "button",
5269
5300
  {
5301
+ ref: buttonRef,
5270
5302
  onClick,
5271
5303
  className: cn(
5272
5304
  "thumbnail-item",
@@ -9489,6 +9521,11 @@ var init_VirtualizedDocumentContainer = __esm({
9489
9521
  selectHighlight,
9490
9522
  activeColor
9491
9523
  } = useHighlights();
9524
+ const [defaultDims, setDefaultDims] = (0, import_react38.useState)({
9525
+ width: DEFAULT_PAGE_WIDTH2,
9526
+ height: DEFAULT_PAGE_HEIGHT2
9527
+ });
9528
+ const [dimsVersion, setDimsVersion] = (0, import_react38.useState)(0);
9492
9529
  (0, import_react38.useEffect)(() => {
9493
9530
  if (document2 !== documentRef.current) {
9494
9531
  documentRef.current = document2;
@@ -9497,39 +9534,47 @@ var init_VirtualizedDocumentContainer = __esm({
9497
9534
  setPageObjects(/* @__PURE__ */ new Map());
9498
9535
  }
9499
9536
  }, [document2]);
9537
+ (0, import_react38.useEffect)(() => {
9538
+ pageDimensionsCache.current.clear();
9539
+ setDimsVersion((v) => v + 1);
9540
+ }, [rotation]);
9500
9541
  (0, import_react38.useEffect)(() => {
9501
9542
  if (!document2 || numPages === 0) return;
9502
- const calculatePageInfos = async () => {
9503
- const infos = [];
9504
- let currentTop = 0;
9505
- for (let i = 1; i <= numPages; i++) {
9506
- let dimensions = pageDimensionsCache.current.get(i);
9507
- if (!dimensions) {
9508
- try {
9509
- const page = pageCache.current.get(i) || await document2.getPage(i);
9510
- if (!pageCache.current.has(i)) {
9511
- pageCache.current.set(i, page);
9512
- }
9513
- const viewport = page.getViewport({ scale: 1, rotation });
9514
- dimensions = { width: viewport.width, height: viewport.height };
9515
- pageDimensionsCache.current.set(i, dimensions);
9516
- } catch {
9517
- dimensions = { width: DEFAULT_PAGE_WIDTH2, height: DEFAULT_PAGE_HEIGHT2 };
9518
- }
9519
- }
9520
- const scaledHeight = Math.floor(dimensions.height * scale);
9521
- infos.push({
9522
- pageNumber: i,
9523
- top: currentTop,
9524
- height: scaledHeight
9525
- });
9526
- currentTop += scaledHeight + pageGap;
9543
+ let cancelled = false;
9544
+ (async () => {
9545
+ try {
9546
+ const page = pageCache.current.get(1) ?? await document2.getPage(1);
9547
+ if (cancelled) return;
9548
+ pageCache.current.set(1, page);
9549
+ const viewport = page.getViewport({ scale: 1, rotation });
9550
+ const dims = { width: viewport.width, height: viewport.height };
9551
+ pageDimensionsCache.current.set(1, dims);
9552
+ setDefaultDims(dims);
9553
+ setDimsVersion((v) => v + 1);
9554
+ } catch {
9527
9555
  }
9528
- setPageInfos(infos);
9529
- setTotalHeight(currentTop - pageGap);
9556
+ })();
9557
+ return () => {
9558
+ cancelled = true;
9530
9559
  };
9531
- calculatePageInfos();
9532
- }, [document2, numPages, scale, rotation, pageGap]);
9560
+ }, [document2, numPages, rotation]);
9561
+ (0, import_react38.useEffect)(() => {
9562
+ if (!document2 || numPages === 0) {
9563
+ setPageInfos([]);
9564
+ setTotalHeight(0);
9565
+ return;
9566
+ }
9567
+ const infos = [];
9568
+ let currentTop = 0;
9569
+ for (let i = 1; i <= numPages; i++) {
9570
+ const dimensions = pageDimensionsCache.current.get(i) ?? defaultDims;
9571
+ const scaledHeight = Math.floor(dimensions.height * scale);
9572
+ infos.push({ pageNumber: i, top: currentTop, height: scaledHeight });
9573
+ currentTop += scaledHeight + pageGap;
9574
+ }
9575
+ setPageInfos(infos);
9576
+ setTotalHeight(Math.max(0, currentTop - pageGap));
9577
+ }, [document2, numPages, scale, rotation, pageGap, defaultDims, dimsVersion]);
9533
9578
  const updateVisiblePages = (0, import_react38.useCallback)(() => {
9534
9579
  if (!scrollContainerRef.current || pageInfos.length === 0) return;
9535
9580
  const container = scrollContainerRef.current;
@@ -9577,6 +9622,7 @@ var init_VirtualizedDocumentContainer = __esm({
9577
9622
  const loadPages = async () => {
9578
9623
  const newPageObjects = new Map(pageObjects);
9579
9624
  let hasChanges = false;
9625
+ let dimsChanged = false;
9580
9626
  for (const pageNum of visiblePages) {
9581
9627
  if (!newPageObjects.has(pageNum)) {
9582
9628
  try {
@@ -9587,6 +9633,14 @@ var init_VirtualizedDocumentContainer = __esm({
9587
9633
  }
9588
9634
  newPageObjects.set(pageNum, page);
9589
9635
  hasChanges = true;
9636
+ if (!pageDimensionsCache.current.has(pageNum)) {
9637
+ const vp = page.getViewport({ scale: 1, rotation });
9638
+ pageDimensionsCache.current.set(pageNum, {
9639
+ width: vp.width,
9640
+ height: vp.height
9641
+ });
9642
+ dimsChanged = true;
9643
+ }
9590
9644
  } catch (error) {
9591
9645
  const errorMessage = error instanceof Error ? error.message : String(error);
9592
9646
  const isDocumentDestroyed = errorMessage.includes("destroyed") || errorMessage.includes("sendWithStream") || errorMessage.includes("sendWithPromise") || errorMessage.includes("Cannot read properties of null");
@@ -9606,9 +9660,12 @@ var init_VirtualizedDocumentContainer = __esm({
9606
9660
  if (hasChanges) {
9607
9661
  setPageObjects(newPageObjects);
9608
9662
  }
9663
+ if (dimsChanged) {
9664
+ setDimsVersion((v) => v + 1);
9665
+ }
9609
9666
  };
9610
9667
  loadPages();
9611
- }, [document2, visiblePages, pageObjects]);
9668
+ }, [document2, visiblePages, pageObjects, rotation]);
9612
9669
  (0, import_react38.useEffect)(() => {
9613
9670
  if (!scrollToPageRequest || !scrollContainerRef.current || pageInfos.length === 0) return;
9614
9671
  const { page, requestId, behavior } = scrollToPageRequest;
@@ -10173,7 +10230,7 @@ var init_DualPageContainer = __esm({
10173
10230
  });
10174
10231
 
10175
10232
  // src/components/PDFViewer/BookModeContainer.tsx
10176
- var import_react41, import_react_pageflip, import_jsx_runtime27, BookPage, BookModeContainer;
10233
+ var import_react41, import_react_pageflip, import_jsx_runtime27, BOOK_MODE_OVERSCAN, BookPage, BookModeContainer;
10177
10234
  var init_BookModeContainer = __esm({
10178
10235
  "src/components/PDFViewer/BookModeContainer.tsx"() {
10179
10236
  "use strict";
@@ -10184,6 +10241,7 @@ var init_BookModeContainer = __esm({
10184
10241
  init_hooks();
10185
10242
  init_utils();
10186
10243
  import_jsx_runtime27 = require("react/jsx-runtime");
10244
+ BOOK_MODE_OVERSCAN = 4;
10187
10245
  BookPage = import_react41.default.forwardRef(function BookPage2({ pageNumber, page, scale, rotation, width, height }, ref) {
10188
10246
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { ref, className: "book-page", "data-page-number": pageNumber, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { style: { width, height, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10189
10247
  PDFPage,
@@ -10216,9 +10274,10 @@ var init_BookModeContainer = __esm({
10216
10274
  } = usePDFViewer();
10217
10275
  const scrollToPageRequest = useViewerStore((s) => s.scrollToPageRequest);
10218
10276
  const { viewerStore } = usePDFViewerStores();
10219
- const [pages, setPages] = (0, import_react41.useState)([]);
10277
+ const [pages, setPages] = (0, import_react41.useState)(/* @__PURE__ */ new Map());
10220
10278
  const [rawPageDims, setRawPageDims] = (0, import_react41.useState)({ width: 612, height: 792 });
10221
- const [isLoadingPages, setIsLoadingPages] = (0, import_react41.useState)(false);
10279
+ const [hasFirstPage, setHasFirstPage] = (0, import_react41.useState)(false);
10280
+ const pageRequestsRef = (0, import_react41.useRef)(/* @__PURE__ */ new Set());
10222
10281
  const containerRef = (0, import_react41.useRef)(null);
10223
10282
  const [containerSize, setContainerSize] = (0, import_react41.useState)({ width: 0, height: 0 });
10224
10283
  const flipBookRef = (0, import_react41.useRef)(null);
@@ -10235,41 +10294,67 @@ var init_BookModeContainer = __esm({
10235
10294
  return () => ro.disconnect();
10236
10295
  }, []);
10237
10296
  (0, import_react41.useEffect)(() => {
10238
- if (!document2) {
10239
- setPages([]);
10240
- return;
10241
- }
10297
+ setPages(/* @__PURE__ */ new Map());
10298
+ pageRequestsRef.current.clear();
10299
+ setHasFirstPage(false);
10300
+ }, [document2]);
10301
+ (0, import_react41.useEffect)(() => {
10302
+ if (!document2 || numPages === 0) return;
10303
+ const start = Math.max(1, currentPage - BOOK_MODE_OVERSCAN);
10304
+ const end = Math.min(numPages, currentPage + BOOK_MODE_OVERSCAN);
10305
+ const isFirstLoad = !hasFirstPage;
10242
10306
  let cancelled = false;
10243
- const loadAllPages = async () => {
10244
- setIsLoadingPages(true);
10245
- try {
10246
- const pagePromises = [];
10247
- for (let i = 1; i <= numPages; i++) {
10248
- pagePromises.push(document2.getPage(i));
10307
+ const wanted = [];
10308
+ if (isFirstLoad && !pages.has(1) && !pageRequestsRef.current.has(1)) {
10309
+ wanted.push(1);
10310
+ pageRequestsRef.current.add(1);
10311
+ }
10312
+ for (let i = start; i <= end; i++) {
10313
+ if (pages.has(i) || pageRequestsRef.current.has(i)) continue;
10314
+ wanted.push(i);
10315
+ pageRequestsRef.current.add(i);
10316
+ }
10317
+ if (wanted.length === 0) return;
10318
+ (async () => {
10319
+ const results = await Promise.allSettled(
10320
+ wanted.map((i) => document2.getPage(i).then((p) => [i, p]))
10321
+ );
10322
+ if (cancelled) return;
10323
+ const next = new Map(pages);
10324
+ let firstPageJustLoaded = null;
10325
+ for (const r of results) {
10326
+ if (r.status === "fulfilled") {
10327
+ const [i, page] = r.value;
10328
+ next.set(i, page);
10329
+ if (i === 1) firstPageJustLoaded = page;
10249
10330
  }
10250
- const results = await Promise.allSettled(pagePromises);
10251
- if (!cancelled) {
10252
- const loaded2 = results.map((r) => r.status === "fulfilled" ? r.value : null);
10253
- setPages(loaded2);
10254
- const firstPage = loaded2[0];
10255
- if (firstPage) {
10256
- const vp = firstPage.getViewport({ scale: 1, rotation });
10257
- setRawPageDims({ width: vp.width, height: vp.height });
10258
- }
10331
+ }
10332
+ for (const i of next.keys()) {
10333
+ if (i !== 1 && (i < start || i > end)) {
10334
+ next.delete(i);
10259
10335
  }
10260
- } catch {
10261
- } finally {
10262
- if (!cancelled) setIsLoadingPages(false);
10263
10336
  }
10264
- };
10265
- loadAllPages();
10337
+ setPages(next);
10338
+ if (firstPageJustLoaded) {
10339
+ const vp = firstPageJustLoaded.getViewport({ scale: 1, rotation });
10340
+ setRawPageDims({ width: vp.width, height: vp.height });
10341
+ setHasFirstPage(true);
10342
+ }
10343
+ for (const i of wanted) {
10344
+ pageRequestsRef.current.delete(i);
10345
+ }
10346
+ })();
10266
10347
  return () => {
10267
10348
  cancelled = true;
10349
+ for (const i of wanted) {
10350
+ pageRequestsRef.current.delete(i);
10351
+ }
10268
10352
  };
10269
- }, [document2, numPages, rotation]);
10353
+ }, [document2, numPages, currentPage, rotation, pages, hasFirstPage]);
10270
10354
  (0, import_react41.useEffect)(() => {
10271
- if (pages[0]) {
10272
- const vp = pages[0].getViewport({ scale: 1, rotation });
10355
+ const firstPage = pages.get(1);
10356
+ if (firstPage) {
10357
+ const vp = firstPage.getViewport({ scale: 1, rotation });
10273
10358
  setRawPageDims({ width: vp.width, height: vp.height });
10274
10359
  }
10275
10360
  }, [pages, rotation]);
@@ -10320,8 +10405,28 @@ var init_BookModeContainer = __esm({
10320
10405
  sepia: "bg-amber-50"
10321
10406
  };
10322
10407
  const themeClass = theme === "dark" ? "dark" : theme === "sepia" ? "sepia" : "";
10323
- const ready = !!document2 && !isLoadingPages && pages.length > 0;
10408
+ const ready = !!document2 && hasFirstPage;
10324
10409
  const hasContainer = containerSize.width > 0 && containerSize.height > 0;
10410
+ const pageChildren = (0, import_react41.useMemo)(() => {
10411
+ if (numPages === 0) return null;
10412
+ const out = new Array(numPages);
10413
+ for (let i = 0; i < numPages; i++) {
10414
+ const pageNumber = i + 1;
10415
+ out[i] = /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10416
+ BookPage,
10417
+ {
10418
+ pageNumber,
10419
+ page: pages.get(pageNumber) ?? null,
10420
+ scale: renderScale,
10421
+ rotation,
10422
+ width: displayWidth,
10423
+ height: displayHeight
10424
+ },
10425
+ pageNumber
10426
+ );
10427
+ }
10428
+ return out;
10429
+ }, [numPages, pages, renderScale, rotation, displayWidth, displayHeight]);
10325
10430
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
10326
10431
  "div",
10327
10432
  {
@@ -10371,18 +10476,7 @@ var init_BookModeContainer = __esm({
10371
10476
  autoSize: false,
10372
10477
  renderOnlyPageLengthChange: false,
10373
10478
  disableFlipByClick: false,
10374
- children: pages.map((page, index) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
10375
- BookPage,
10376
- {
10377
- pageNumber: index + 1,
10378
- page,
10379
- scale: renderScale,
10380
- rotation,
10381
- width: displayWidth,
10382
- height: displayHeight
10383
- },
10384
- index
10385
- ))
10479
+ children: pageChildren
10386
10480
  }
10387
10481
  )
10388
10482
  ]