canvu-react 0.3.12 → 0.3.14

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
@@ -236,10 +236,32 @@ async function runWithConcurrency(items, concurrency, execute) {
236
236
  async function loadPdfToStore(file, store, options) {
237
237
  const scale = options?.scale ?? 1.5;
238
238
  const pageConcurrency = options?.pageConcurrency ?? 2;
239
+ const storeThumbnails = options?.storeThumbnails ?? false;
239
240
  const pdfjs = await getPdfJs();
240
241
  const arrayBuffer = await file.arrayBuffer();
241
242
  const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
242
243
  const pageNumbers = normalizePdfPageNumbers(options?.pageNumbers, pdf.numPages);
244
+ const bufferedResults = /* @__PURE__ */ new Map();
245
+ let nextEmitIndex = 0;
246
+ let emitChain = Promise.resolve();
247
+ const queuePageEmission = async (pageResult) => {
248
+ bufferedResults.set(pageResult.pageNumber, pageResult);
249
+ const run = async () => {
250
+ while (nextEmitIndex < pageNumbers.length) {
251
+ const nextPageNumber = pageNumbers[nextEmitIndex];
252
+ if (nextPageNumber == null) break;
253
+ const bufferedResult = bufferedResults.get(nextPageNumber);
254
+ if (!bufferedResult) break;
255
+ bufferedResults.delete(nextPageNumber);
256
+ nextEmitIndex += 1;
257
+ await options?.onPageStored?.(bufferedResult);
258
+ }
259
+ };
260
+ const nextChain = emitChain.then(run, run);
261
+ emitChain = nextChain.catch(() => {
262
+ });
263
+ await nextChain;
264
+ };
243
265
  return await runWithConcurrency(
244
266
  pageNumbers,
245
267
  pageConcurrency,
@@ -249,27 +271,31 @@ async function loadPdfToStore(file, store, options) {
249
271
  const mime = "image/png";
250
272
  const pageBlob = await canvasToBlob(canvas, mime);
251
273
  const blobId = await store.storeOriginal(pageBlob);
252
- const thumbScale = Math.min(1, 256 / Math.max(width, height));
253
- const tw = Math.max(1, Math.round(width * thumbScale));
254
- const th = Math.max(1, Math.round(height * thumbScale));
255
- const thumbCanvas = document.createElement("canvas");
256
- thumbCanvas.width = tw;
257
- thumbCanvas.height = th;
258
- const tCtx = thumbCanvas.getContext("2d");
259
- if (tCtx) {
260
- tCtx.imageSmoothingEnabled = true;
261
- tCtx.imageSmoothingQuality = "high";
262
- tCtx.drawImage(canvas, 0, 0, tw, th);
263
- }
264
- const thumbBlob = await canvasToBlob(thumbCanvas, mime);
265
- const thumbnailBlobId = await store.storeThumbnail(thumbBlob);
266
- return {
274
+ const thumbnailBlobId = storeThumbnails ? await (async () => {
275
+ const thumbScale = Math.min(1, 256 / Math.max(width, height));
276
+ const tw = Math.max(1, Math.round(width * thumbScale));
277
+ const th = Math.max(1, Math.round(height * thumbScale));
278
+ const thumbCanvas = document.createElement("canvas");
279
+ thumbCanvas.width = tw;
280
+ thumbCanvas.height = th;
281
+ const tCtx = thumbCanvas.getContext("2d");
282
+ if (tCtx) {
283
+ tCtx.imageSmoothingEnabled = true;
284
+ tCtx.imageSmoothingQuality = "high";
285
+ tCtx.drawImage(canvas, 0, 0, tw, th);
286
+ }
287
+ const thumbBlob = await canvasToBlob(thumbCanvas, mime);
288
+ return await store.storeThumbnail(thumbBlob);
289
+ })() : "";
290
+ const pageResult = {
267
291
  blobId,
268
292
  thumbnailBlobId,
269
293
  width,
270
294
  height,
271
295
  pageNumber
272
296
  };
297
+ await queuePageEmission(pageResult);
298
+ return pageResult;
273
299
  }
274
300
  );
275
301
  }
@@ -1706,7 +1732,8 @@ function collectEraserTargetsAtWorldPoint(items, worldX, worldY, options) {
1706
1732
  var HYDRATION_CACHE_NAME = "canvu-asset-hydration-v1";
1707
1733
  var DEFAULT_PDF_SCALE = 1.15;
1708
1734
  var DEFAULT_PDF_PAGE_CONCURRENCY = 2;
1709
- var hydrationBlobPromises = /* @__PURE__ */ new Map();
1735
+ var hydrationSourcePromises = /* @__PURE__ */ new Map();
1736
+ var pdfSourceBlobPromises = /* @__PURE__ */ new Map();
1710
1737
  function buildImageHydrationKey(assetId) {
1711
1738
  return `image:${assetId}`;
1712
1739
  }
@@ -1754,23 +1781,38 @@ async function fetchBlob(url) {
1754
1781
  return null;
1755
1782
  }
1756
1783
  }
1757
- async function getHydrationBlob(key, preferCachedRasters, loader) {
1784
+ async function getHydrationSource(key, preferCachedRasters, loader) {
1758
1785
  const cached = preferCachedRasters ? await readCachedHydrationBlob(key) : null;
1759
- if (cached) return cached;
1760
- const inFlight = hydrationBlobPromises.get(key);
1786
+ if (cached) {
1787
+ return {
1788
+ blob: cached
1789
+ };
1790
+ }
1791
+ const inFlight = hydrationSourcePromises.get(key);
1761
1792
  if (inFlight) return await inFlight;
1762
1793
  const nextPromise = (async () => {
1763
- const blob = await loader();
1764
- if (blob) {
1765
- await writeCachedHydrationBlob(key, blob);
1794
+ const source = await loader();
1795
+ if (source?.blob) {
1796
+ await writeCachedHydrationBlob(key, source.blob);
1766
1797
  }
1767
- return blob;
1798
+ return source;
1768
1799
  })();
1769
- hydrationBlobPromises.set(key, nextPromise);
1800
+ hydrationSourcePromises.set(key, nextPromise);
1801
+ try {
1802
+ return await nextPromise;
1803
+ } finally {
1804
+ hydrationSourcePromises.delete(key);
1805
+ }
1806
+ }
1807
+ async function getPdfSourceBlob(url) {
1808
+ const inFlight = pdfSourceBlobPromises.get(url);
1809
+ if (inFlight) return await inFlight;
1810
+ const nextPromise = fetchBlob(url);
1811
+ pdfSourceBlobPromises.set(url, nextPromise);
1770
1812
  try {
1771
1813
  return await nextPromise;
1772
1814
  } finally {
1773
- hydrationBlobPromises.delete(key);
1815
+ pdfSourceBlobPromises.delete(url);
1774
1816
  }
1775
1817
  }
1776
1818
  function registerObjectUrl(objectUrls, blob) {
@@ -1824,18 +1866,22 @@ async function hydrateImageAssets(requests, resolvedAssetUrls, objectUrls, prefe
1824
1866
  if (!resolvedAsset?.url) {
1825
1867
  return [assetId, null];
1826
1868
  }
1827
- const blob = await getHydrationBlob(
1869
+ const source = await getHydrationSource(
1828
1870
  buildImageHydrationKey(assetId),
1829
1871
  preferCachedRasters,
1830
- async () => await fetchBlob(resolvedAsset.url)
1872
+ async () => {
1873
+ const blob = await fetchBlob(resolvedAsset.url);
1874
+ if (!blob) return null;
1875
+ return { blob };
1876
+ }
1831
1877
  );
1832
- if (!blob) {
1878
+ if (!source?.blob) {
1833
1879
  return [assetId, null];
1834
1880
  }
1835
1881
  return [
1836
1882
  assetId,
1837
1883
  {
1838
- href: registerObjectUrl(objectUrls, blob)
1884
+ href: registerObjectUrl(objectUrls, source.blob)
1839
1885
  }
1840
1886
  ];
1841
1887
  })
@@ -1850,45 +1896,69 @@ async function hydratePdfAssets(requests, resolvedAssetUrls, objectUrls, options
1850
1896
  if (!resolvedAsset?.url) {
1851
1897
  continue;
1852
1898
  }
1853
- const missingPages = [];
1854
- for (const pageNumber of group.pageNumbers) {
1855
- const cacheKey = buildPdfHydrationKey(group.assetId, pageNumber, group.scale);
1856
- const cachedBlob = await readCachedHydrationBlob(cacheKey);
1857
- if (!cachedBlob) {
1858
- missingPages.push(pageNumber);
1899
+ const pageKeys = group.pageNumbers.map((pageNumber) => ({
1900
+ pageNumber,
1901
+ cacheKey: buildPdfHydrationKey(group.assetId, pageNumber, group.scale)
1902
+ }));
1903
+ const pagesToRender = [];
1904
+ for (const { pageNumber, cacheKey } of pageKeys) {
1905
+ const cachedBlob = options.preferCachedRasters ? await readCachedHydrationBlob(cacheKey) : null;
1906
+ if (cachedBlob) {
1907
+ hydratedPages.set(cacheKey, {
1908
+ href: registerObjectUrl(objectUrls, cachedBlob)
1909
+ });
1859
1910
  continue;
1860
1911
  }
1861
- hydratedPages.set(cacheKey, {
1862
- href: registerObjectUrl(objectUrls, cachedBlob)
1863
- });
1864
- }
1865
- if (missingPages.length === 0) {
1866
- continue;
1912
+ if (!hydrationSourcePromises.has(cacheKey)) {
1913
+ pagesToRender.push({ pageNumber, cacheKey });
1914
+ }
1867
1915
  }
1868
- const pdfBlob = await fetchBlob(resolvedAsset.url);
1869
- if (!pdfBlob) {
1870
- continue;
1916
+ if (pagesToRender.length > 0) {
1917
+ const renderPromise = (async () => {
1918
+ const pdfBlob = await getPdfSourceBlob(resolvedAsset.url);
1919
+ if (!pdfBlob) {
1920
+ return /* @__PURE__ */ new Map();
1921
+ }
1922
+ const renderedPages = await loadPdfToStore(pdfBlob, options.imageStore, {
1923
+ scale: group.scale,
1924
+ pageNumbers: pagesToRender.map(({ pageNumber }) => pageNumber),
1925
+ pageConcurrency: options.pdfPageConcurrency
1926
+ });
1927
+ const renderedByPage = /* @__PURE__ */ new Map();
1928
+ for (const renderedPage of renderedPages) {
1929
+ const pageBlob = await options.imageStore.getOriginal(renderedPage.blobId);
1930
+ renderedByPage.set(
1931
+ renderedPage.pageNumber,
1932
+ pageBlob ? {
1933
+ blob: pageBlob,
1934
+ width: renderedPage.width,
1935
+ height: renderedPage.height
1936
+ } : null
1937
+ );
1938
+ }
1939
+ return renderedByPage;
1940
+ })();
1941
+ for (const { pageNumber, cacheKey } of pagesToRender) {
1942
+ const pagePromise = getHydrationSource(
1943
+ cacheKey,
1944
+ options.preferCachedRasters,
1945
+ async () => (await renderPromise).get(pageNumber) ?? null
1946
+ );
1947
+ hydrationSourcePromises.set(cacheKey, pagePromise);
1948
+ }
1871
1949
  }
1872
- const renderedPages = await loadPdfToStore(pdfBlob, options.imageStore, {
1873
- scale: group.scale,
1874
- pageNumbers: missingPages,
1875
- pageConcurrency: options.pdfPageConcurrency
1876
- });
1877
- for (const renderedPage of renderedPages) {
1878
- const cacheKey = buildPdfHydrationKey(
1879
- group.assetId,
1880
- renderedPage.pageNumber,
1881
- group.scale
1950
+ for (const { cacheKey } of pageKeys) {
1951
+ if (hydratedPages.has(cacheKey)) continue;
1952
+ const source = await getHydrationSource(
1953
+ cacheKey,
1954
+ options.preferCachedRasters,
1955
+ async () => null
1882
1956
  );
1883
- const pageBlob = await options.imageStore.getOriginal(renderedPage.blobId);
1884
- if (!pageBlob) {
1885
- continue;
1886
- }
1887
- await writeCachedHydrationBlob(cacheKey, pageBlob);
1957
+ if (!source?.blob) continue;
1888
1958
  hydratedPages.set(cacheKey, {
1889
- href: registerObjectUrl(objectUrls, pageBlob),
1890
- width: renderedPage.width,
1891
- height: renderedPage.height
1959
+ href: registerObjectUrl(objectUrls, source.blob),
1960
+ width: source.width,
1961
+ height: source.height
1892
1962
  });
1893
1963
  }
1894
1964
  }
@@ -1929,7 +1999,9 @@ async function hydrateSceneItemsWithAssets(items, assetStore, options = {}) {
1929
1999
  objectUrls,
1930
2000
  {
1931
2001
  imageStore,
1932
- pdfPageConcurrency}
2002
+ pdfPageConcurrency,
2003
+ preferCachedRasters
2004
+ }
1933
2005
  );
1934
2006
  return {
1935
2007
  items: items.map((item) => {