canvu-react 0.4.75 → 0.4.77

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.js CHANGED
@@ -203,342 +203,6 @@ var IndexedDbImageStore = class {
203
203
  }
204
204
  };
205
205
 
206
- // src/image/pdf-worker-renderer.ts
207
- var PDF_WORKER_SOURCE = `
208
- let pdfjsPromise = null;
209
-
210
- const loadPdfJs = async (pdfjsModuleCandidates) => {
211
- if (!pdfjsPromise) {
212
- pdfjsPromise = (async () => {
213
- let lastError;
214
- for (const candidate of pdfjsModuleCandidates) {
215
- try {
216
- const pdfjs = await import(candidate.moduleUrl);
217
- if (pdfjs.GlobalWorkerOptions) {
218
- pdfjs.GlobalWorkerOptions.workerSrc = candidate.workerUrl;
219
- }
220
- return pdfjs;
221
- } catch (error) {
222
- lastError = error;
223
- }
224
- }
225
- throw lastError ?? new Error("Unable to load pdfjs-dist");
226
- })();
227
- }
228
- return await pdfjsPromise;
229
- };
230
-
231
- const createErrorPayload = (error) => ({
232
- message: error instanceof Error ? error.message : String(error),
233
- name: error instanceof Error ? error.name : "Error",
234
- stack: error instanceof Error ? error.stack : undefined,
235
- });
236
-
237
- const normalizePdfPageNumbers = (pageNumbers, pageCount) => {
238
- if (!Array.isArray(pageNumbers) || pageNumbers.length === 0) {
239
- return Array.from({ length: pageCount }, (_, index) => index + 1);
240
- }
241
- return [...new Set(pageNumbers)]
242
- .filter((pageNumber) => pageNumber >= 1 && pageNumber <= pageCount)
243
- .sort((left, right) => left - right);
244
- };
245
-
246
- const runWithConcurrency = async (items, concurrency, execute) => {
247
- const results = new Array(items.length);
248
- let nextIndex = 0;
249
- const safeConcurrency = Number.isFinite(concurrency) && concurrency > 0
250
- ? Math.round(concurrency)
251
- : 1;
252
- const workerCount = Math.max(1, Math.min(safeConcurrency, items.length));
253
- await Promise.all(
254
- Array.from({ length: workerCount }, async () => {
255
- while (nextIndex < items.length) {
256
- const currentIndex = nextIndex;
257
- nextIndex += 1;
258
- const item = items[currentIndex];
259
- if (item === undefined) {
260
- continue;
261
- }
262
- results[currentIndex] = await execute(item);
263
- }
264
- }),
265
- );
266
- return results;
267
- };
268
-
269
- const renderPageToBlob = async (page, scale, storeThumbnails) => {
270
- const raw = page.getViewport({ scale: 1 });
271
- const adjustedScale = Math.round(raw.width * scale) / raw.width;
272
- const viewport = page.getViewport({ scale: adjustedScale });
273
- const width = Math.round(viewport.width);
274
- const height = Math.round(viewport.height);
275
- const canvas = new OffscreenCanvas(width, height);
276
- const canvasContext = canvas.getContext("2d");
277
- if (!canvasContext) {
278
- throw new Error("OffscreenCanvas 2D context unavailable");
279
- }
280
- canvasContext.imageSmoothingEnabled = true;
281
- canvasContext.imageSmoothingQuality = "high";
282
- await page.render({ canvasContext, viewport }).promise;
283
- const blob = await canvas.convertToBlob({ type: "image/png" });
284
- let thumbnailBlob;
285
- if (storeThumbnails) {
286
- const thumbScale = Math.min(1, 256 / Math.max(width, height));
287
- const thumbnailWidth = Math.max(1, Math.round(width * thumbScale));
288
- const thumbnailHeight = Math.max(1, Math.round(height * thumbScale));
289
- const thumbnailCanvas = new OffscreenCanvas(thumbnailWidth, thumbnailHeight);
290
- const thumbnailContext = thumbnailCanvas.getContext("2d");
291
- if (thumbnailContext) {
292
- thumbnailContext.imageSmoothingEnabled = true;
293
- thumbnailContext.imageSmoothingQuality = "high";
294
- thumbnailContext.drawImage(canvas, 0, 0, thumbnailWidth, thumbnailHeight);
295
- }
296
- thumbnailBlob = await thumbnailCanvas.convertToBlob({ type: "image/png" });
297
- }
298
- return { blob, height, thumbnailBlob, width };
299
- };
300
-
301
- self.onmessage = async (event) => {
302
- const message = event.data;
303
- if (!message || message.type !== "render") {
304
- return;
305
- }
306
- try {
307
- const pdfjs = await loadPdfJs(message.pdfjsModuleCandidates);
308
- const pdf = await pdfjs.getDocument({
309
- data: new Uint8Array(message.pdfData),
310
- disableWorker: true,
311
- }).promise;
312
- try {
313
- const pageNumbers = normalizePdfPageNumbers(message.pageNumbers, pdf.numPages);
314
- const bufferedPages = new Map();
315
- let nextEmitIndex = 0;
316
- let emitChain = Promise.resolve();
317
- const emitPageInOrder = async (pageNumber, renderedPage) => {
318
- bufferedPages.set(pageNumber, renderedPage);
319
- const flush = () => {
320
- while (nextEmitIndex < pageNumbers.length) {
321
- const nextPageNumber = pageNumbers[nextEmitIndex];
322
- if (nextPageNumber == null) break;
323
- const nextPage = bufferedPages.get(nextPageNumber);
324
- if (!nextPage) break;
325
- bufferedPages.delete(nextPageNumber);
326
- nextEmitIndex += 1;
327
- self.postMessage({
328
- type: "page",
329
- page: {
330
- ...nextPage,
331
- pageNumber: nextPageNumber,
332
- },
333
- });
334
- }
335
- };
336
- const nextChain = emitChain.then(flush, flush);
337
- emitChain = nextChain.catch(() => {});
338
- await nextChain;
339
- };
340
- await runWithConcurrency(
341
- pageNumbers,
342
- message.pageConcurrency,
343
- async (pageNumber) => {
344
- const page = await pdf.getPage(pageNumber);
345
- try {
346
- const renderedPage = await renderPageToBlob(
347
- page,
348
- message.scale,
349
- message.storeThumbnails,
350
- );
351
- await emitPageInOrder(pageNumber, renderedPage);
352
- } finally {
353
- page.cleanup();
354
- }
355
- },
356
- );
357
- await emitChain;
358
- } finally {
359
- await pdf.destroy();
360
- }
361
- self.postMessage({ type: "done" });
362
- } catch (error) {
363
- self.postMessage({ type: "error", error: createErrorPayload(error) });
364
- }
365
- };
366
- `;
367
- var isRecord = (value) => typeof value === "object" && value !== null;
368
- var hasObjectMembers = (value) => (typeof value === "object" || typeof value === "function") && value !== null;
369
- var isOffscreenCanvasCandidate = (value) => typeof value === "function";
370
- var getEnvironmentRecord = (environment) => isRecord(environment) ? environment : null;
371
- var canUsePdfWorkerRenderer = (environment = globalThis) => {
372
- const candidateEnvironment = getEnvironmentRecord(environment);
373
- if (!candidateEnvironment) return false;
374
- if (typeof candidateEnvironment.Worker !== "function") return false;
375
- if (typeof candidateEnvironment.Blob !== "function") return false;
376
- if (!isOffscreenCanvasCandidate(candidateEnvironment.OffscreenCanvas)) {
377
- return false;
378
- }
379
- const urlCandidate = candidateEnvironment.URL;
380
- if (!hasObjectMembers(urlCandidate)) return false;
381
- if (typeof urlCandidate.createObjectURL !== "function") return false;
382
- if (typeof urlCandidate.revokeObjectURL !== "function") return false;
383
- try {
384
- const canvas = new candidateEnvironment.OffscreenCanvas(1, 1);
385
- return typeof canvas.getContext === "function" && typeof canvas.convertToBlob === "function";
386
- } catch {
387
- return false;
388
- }
389
- };
390
- var createAbortError = () => new DOMException("Aborted", "AbortError");
391
- var throwIfAborted = (signal) => {
392
- if (signal?.aborted) {
393
- throw createAbortError();
394
- }
395
- };
396
- var createPdfWorker = () => {
397
- const workerBlob = new Blob([PDF_WORKER_SOURCE], {
398
- type: "text/javascript"
399
- });
400
- const workerUrl = URL.createObjectURL(workerBlob);
401
- try {
402
- const worker = new Worker(workerUrl, {
403
- name: "canvu-pdf-renderer",
404
- type: "module"
405
- });
406
- return {
407
- release: () => URL.revokeObjectURL(workerUrl),
408
- worker
409
- };
410
- } catch (error) {
411
- URL.revokeObjectURL(workerUrl);
412
- throw error;
413
- }
414
- };
415
- var createWorkerError = (message) => {
416
- const error = new Error(message.error.message);
417
- error.name = message.error.name;
418
- error.stack = message.error.stack;
419
- return error;
420
- };
421
- var PACKAGED_PDFJS_MODULE_FILE = "./pdf.mjs";
422
- var PACKAGED_PDFJS_WORKER_FILE = "./pdf.worker.mjs";
423
- var resolveRelativeAssetUrl = (path, baseUrl) => new URL(path, baseUrl).toString();
424
- var resolvePackagedPdfJsModuleCandidate = (baseUrl = import.meta.url) => ({
425
- moduleUrl: resolveRelativeAssetUrl(PACKAGED_PDFJS_MODULE_FILE, baseUrl),
426
- workerUrl: resolveRelativeAssetUrl(PACKAGED_PDFJS_WORKER_FILE, baseUrl)
427
- });
428
- var resolveBundledPdfJsModuleCandidate = () => ({
429
- moduleUrl: new URL("pdfjs-dist/build/pdf.min.mjs", import.meta.url).toString(),
430
- workerUrl: new URL(
431
- "pdfjs-dist/build/pdf.worker.min.mjs",
432
- import.meta.url
433
- ).toString()
434
- });
435
- var resolvePdfJsModuleCandidates = () => [
436
- resolvePackagedPdfJsModuleCandidate(),
437
- resolveBundledPdfJsModuleCandidate()
438
- ];
439
- var loadPdfToStoreWithWorker = async (file, store, options) => {
440
- throwIfAborted(options.signal);
441
- const arrayBuffer = await file.arrayBuffer();
442
- throwIfAborted(options.signal);
443
- return await new Promise((resolve, reject) => {
444
- const { release, worker } = createPdfWorker();
445
- const pageResults = [];
446
- let storeChain = Promise.resolve();
447
- let cleanedUp = false;
448
- let settled = false;
449
- const cleanup = () => {
450
- if (cleanedUp) return;
451
- cleanedUp = true;
452
- options.signal?.removeEventListener("abort", abortWorker);
453
- worker.onmessage = null;
454
- worker.onerror = null;
455
- worker.terminate();
456
- release();
457
- };
458
- const rejectOnce = (error) => {
459
- if (settled) return;
460
- settled = true;
461
- cleanup();
462
- reject(error);
463
- };
464
- const rejectAfterQueuedStores = (error) => {
465
- if (settled) return;
466
- cleanup();
467
- storeChain.then(() => {
468
- if (settled) return;
469
- settled = true;
470
- reject(error);
471
- }).catch(rejectOnce);
472
- };
473
- function abortWorker() {
474
- rejectOnce(createAbortError());
475
- }
476
- const storePage = (message) => {
477
- try {
478
- options.onPageReceived?.({ pageNumber: message.page.pageNumber });
479
- } catch (error) {
480
- rejectOnce(error);
481
- return;
482
- }
483
- storeChain = storeChain.then(async () => {
484
- throwIfAborted(options.signal);
485
- const blobId = await store.storeOriginal(message.page.blob);
486
- throwIfAborted(options.signal);
487
- const thumbnailBlobId = options.storeThumbnails && message.page.thumbnailBlob ? await store.storeThumbnail(message.page.thumbnailBlob) : "";
488
- throwIfAborted(options.signal);
489
- const pageResult = {
490
- blobId,
491
- height: message.page.height,
492
- pageNumber: message.page.pageNumber,
493
- thumbnailBlobId,
494
- width: message.page.width
495
- };
496
- pageResults.push(pageResult);
497
- await options.onPageStored?.(pageResult);
498
- throwIfAborted(options.signal);
499
- });
500
- void storeChain.catch(rejectOnce);
501
- };
502
- worker.onmessage = (event) => {
503
- const message = event.data;
504
- if (message.type === "page") {
505
- storePage(message);
506
- return;
507
- }
508
- if (message.type === "error") {
509
- rejectAfterQueuedStores(createWorkerError(message));
510
- return;
511
- }
512
- storeChain.then(() => {
513
- if (settled) return;
514
- settled = true;
515
- cleanup();
516
- resolve(pageResults);
517
- }).catch(rejectOnce);
518
- };
519
- worker.onerror = (event) => {
520
- rejectAfterQueuedStores(event.error ?? new Error(event.message));
521
- };
522
- options.signal?.addEventListener("abort", abortWorker, { once: true });
523
- try {
524
- worker.postMessage(
525
- {
526
- pdfData: arrayBuffer,
527
- pdfjsModuleCandidates: resolvePdfJsModuleCandidates(),
528
- pageNumbers: options.pageNumbers ? [...options.pageNumbers] : void 0,
529
- pageConcurrency: options.pageConcurrency,
530
- scale: options.scale,
531
- storeThumbnails: options.storeThumbnails,
532
- type: "render"
533
- },
534
- [arrayBuffer]
535
- );
536
- } catch (error) {
537
- rejectOnce(error);
538
- }
539
- });
540
- };
541
-
542
206
  // src/image/pdf-loader.ts
543
207
  var pdfjsPromise = null;
544
208
  function getPdfJs() {
@@ -555,7 +219,7 @@ function getPdfJs() {
555
219
  return pdfjsPromise;
556
220
  }
557
221
  async function renderPageToCanvas(page, scale, signal) {
558
- throwIfAborted2(signal);
222
+ throwIfAborted(signal);
559
223
  const raw = page.getViewport({ scale: 1 });
560
224
  const adjustedScale = Math.round(raw.width * scale) / raw.width;
561
225
  const viewport = page.getViewport({ scale: adjustedScale });
@@ -577,7 +241,7 @@ async function renderPageToCanvas(page, scale, signal) {
577
241
  try {
578
242
  await renderTask.promise;
579
243
  } catch (error) {
580
- if (signal.aborted) throw createAbortError2();
244
+ if (signal.aborted) throw createAbortError();
581
245
  throw error;
582
246
  } finally {
583
247
  signal.removeEventListener("abort", abortRender);
@@ -585,15 +249,15 @@ async function renderPageToCanvas(page, scale, signal) {
585
249
  } else {
586
250
  await renderTask.promise;
587
251
  }
588
- throwIfAborted2(signal);
252
+ throwIfAborted(signal);
589
253
  return { canvas, width: w, height: h };
590
254
  }
591
- function createAbortError2() {
255
+ function createAbortError() {
592
256
  return new DOMException("Aborted", "AbortError");
593
257
  }
594
- function throwIfAborted2(signal) {
258
+ function throwIfAborted(signal) {
595
259
  if (signal?.aborted) {
596
- throw createAbortError2();
260
+ throw createAbortError();
597
261
  }
598
262
  }
599
263
  function normalizePdfPageNumbers(pageNumbers, pageCount) {
@@ -609,7 +273,7 @@ async function runWithConcurrency(items, concurrency, execute, signal) {
609
273
  await Promise.all(
610
274
  Array.from({ length: workerCount }, async () => {
611
275
  while (nextIndex < items.length) {
612
- throwIfAborted2(signal);
276
+ throwIfAborted(signal);
613
277
  const currentIndex = nextIndex;
614
278
  nextIndex += 1;
615
279
  const item = items[currentIndex];
@@ -617,7 +281,7 @@ async function runWithConcurrency(items, concurrency, execute, signal) {
617
281
  continue;
618
282
  }
619
283
  results[currentIndex] = await execute(item);
620
- throwIfAborted2(signal);
284
+ throwIfAborted(signal);
621
285
  }
622
286
  })
623
287
  );
@@ -628,52 +292,11 @@ async function loadPdfToStore(file, store, options) {
628
292
  const pageConcurrency = options?.pageConcurrency ?? 2;
629
293
  const storeThumbnails = options?.storeThumbnails ?? false;
630
294
  const signal = options?.signal;
631
- throwIfAborted2(signal);
632
- if (canUsePdfWorkerRenderer()) {
633
- let workerPageCount = 0;
634
- try {
635
- return await loadPdfToStoreWithWorker(file, store, {
636
- scale,
637
- pageNumbers: options?.pageNumbers,
638
- pageConcurrency,
639
- storeThumbnails,
640
- signal,
641
- onPageReceived: () => {
642
- workerPageCount += 1;
643
- },
644
- onPageStored: async (page) => {
645
- await options?.onPageStored?.(page);
646
- }
647
- });
648
- } catch (error) {
649
- if (signal?.aborted || workerPageCount > 0) {
650
- throw error;
651
- }
652
- }
653
- }
654
- return await loadPdfToStoreOnMainThread(file, store, {
655
- scale,
656
- pageNumbers: options?.pageNumbers,
657
- pageConcurrency,
658
- storeThumbnails,
659
- signal,
660
- onPageStored: options?.onPageStored
661
- });
662
- }
663
- async function loadPdfToStoreOnMainThread(file, store, options) {
664
- const {
665
- pageConcurrency,
666
- scale,
667
- signal,
668
- storeThumbnails,
669
- onPageStored,
670
- pageNumbers
671
- } = options;
672
- throwIfAborted2(signal);
295
+ throwIfAborted(signal);
673
296
  const pdfjs = await getPdfJs();
674
- throwIfAborted2(signal);
297
+ throwIfAborted(signal);
675
298
  const arrayBuffer = await file.arrayBuffer();
676
- throwIfAborted2(signal);
299
+ throwIfAborted(signal);
677
300
  const loadingTask = pdfjs.getDocument({ data: arrayBuffer });
678
301
  if (signal) {
679
302
  const abortLoading = () => {
@@ -685,14 +308,14 @@ async function loadPdfToStoreOnMainThread(file, store, options) {
685
308
  signal.removeEventListener("abort", abortLoading);
686
309
  return await loadPdfDocumentToStore(pdf2, store, {
687
310
  scale,
688
- pageNumbers,
311
+ pageNumbers: options?.pageNumbers,
689
312
  pageConcurrency,
690
313
  storeThumbnails,
691
314
  signal,
692
- onPageStored
315
+ onPageStored: options?.onPageStored
693
316
  });
694
317
  } catch (error) {
695
- if (signal.aborted) throw createAbortError2();
318
+ if (signal.aborted) throw createAbortError();
696
319
  throw error;
697
320
  } finally {
698
321
  signal.removeEventListener("abort", abortLoading);
@@ -701,16 +324,16 @@ async function loadPdfToStoreOnMainThread(file, store, options) {
701
324
  const pdf = await loadingTask.promise;
702
325
  return await loadPdfDocumentToStore(pdf, store, {
703
326
  scale,
704
- pageNumbers,
327
+ pageNumbers: options?.pageNumbers,
705
328
  pageConcurrency,
706
329
  storeThumbnails,
707
- onPageStored
330
+ onPageStored: options?.onPageStored
708
331
  });
709
332
  }
710
333
  async function loadPdfDocumentToStore(pdf, store, options) {
711
334
  const { pageConcurrency, scale, signal } = options;
712
335
  const storeThumbnails = options.storeThumbnails ?? false;
713
- throwIfAborted2(signal);
336
+ throwIfAborted(signal);
714
337
  const pageNumbers = normalizePdfPageNumbers(options?.pageNumbers, pdf.numPages);
715
338
  const bufferedResults = /* @__PURE__ */ new Map();
716
339
  let nextEmitIndex = 0;
@@ -725,9 +348,9 @@ async function loadPdfDocumentToStore(pdf, store, options) {
725
348
  if (!bufferedResult) break;
726
349
  bufferedResults.delete(nextPageNumber);
727
350
  nextEmitIndex += 1;
728
- throwIfAborted2(signal);
351
+ throwIfAborted(signal);
729
352
  await options?.onPageStored?.(bufferedResult);
730
- throwIfAborted2(signal);
353
+ throwIfAborted(signal);
731
354
  }
732
355
  };
733
356
  const nextChain = emitChain.then(run, run);
@@ -739,20 +362,20 @@ async function loadPdfDocumentToStore(pdf, store, options) {
739
362
  pageNumbers,
740
363
  pageConcurrency,
741
364
  async (pageNumber) => {
742
- throwIfAborted2(signal);
365
+ throwIfAborted(signal);
743
366
  const page = await pdf.getPage(pageNumber);
744
- throwIfAborted2(signal);
367
+ throwIfAborted(signal);
745
368
  const { canvas, width, height } = await renderPageToCanvas(
746
369
  page,
747
370
  scale,
748
371
  signal
749
372
  );
750
- throwIfAborted2(signal);
373
+ throwIfAborted(signal);
751
374
  const mime = "image/png";
752
375
  const pageBlob = await encodeCanvasToBlob(canvas, { mimeType: mime });
753
- throwIfAborted2(signal);
376
+ throwIfAborted(signal);
754
377
  const blobId = await store.storeOriginal(pageBlob);
755
- throwIfAborted2(signal);
378
+ throwIfAborted(signal);
756
379
  const thumbnailBlobId = storeThumbnails ? await (async () => {
757
380
  const thumbScale = Math.min(1, 256 / Math.max(width, height));
758
381
  const tw = Math.max(1, Math.round(width * thumbScale));
@@ -769,7 +392,7 @@ async function loadPdfDocumentToStore(pdf, store, options) {
769
392
  const thumbBlob = await encodeCanvasToBlob(thumbCanvas, {
770
393
  mimeType: mime
771
394
  });
772
- throwIfAborted2(signal);
395
+ throwIfAborted(signal);
773
396
  return await store.storeThumbnail(thumbBlob);
774
397
  })() : "";
775
398
  const pageResult = {
@@ -3193,6 +2816,9 @@ function shouldRedrawRasterImageCanvas({
3193
2816
  if (currentSourceKey !== nextSourceKey && safeCurrentWidth === safeNextWidth && safeCurrentHeight === safeNextHeight) {
3194
2817
  return true;
3195
2818
  }
2819
+ if (currentSourceKey !== nextSourceKey && (safeNextWidth < safeCurrentWidth || safeNextHeight < safeCurrentHeight)) {
2820
+ return true;
2821
+ }
3196
2822
  return safeNextWidth > safeCurrentWidth * upscaleRedrawRatio || safeNextHeight > safeCurrentHeight * upscaleRedrawRatio;
3197
2823
  }
3198
2824
  function toPositiveFiniteNumber(value, fallback) {