canvu-react 0.3.13 → 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/README.md +9 -1
- package/dist/index.cjs +41 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +41 -15
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +100 -67
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +9 -1
- package/dist/react.d.ts +9 -1
- package/dist/react.js +100 -67
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/react.d.cts
CHANGED
|
@@ -107,10 +107,18 @@ type IngestAssetFilesToSceneItemsOptions = {
|
|
|
107
107
|
/**
|
|
108
108
|
* PDF rasterization scale forwarded to `pdfjs-dist`.
|
|
109
109
|
*
|
|
110
|
-
* Defaults to `1.
|
|
110
|
+
* Defaults to `1.15` to balance first paint latency with zoom quality.
|
|
111
111
|
*/
|
|
112
112
|
pdfScale?: number;
|
|
113
113
|
pdfPageConcurrency?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Called as soon as canvu finishes converting one or more items from the
|
|
116
|
+
* source files.
|
|
117
|
+
*/
|
|
118
|
+
onItemsReady?: (items: VectorSceneItem[], context: {
|
|
119
|
+
file: File;
|
|
120
|
+
kind: VectorViewportAssetKind;
|
|
121
|
+
}) => void;
|
|
114
122
|
/**
|
|
115
123
|
* Final hook to customize each created item after canvu attaches its own
|
|
116
124
|
* image metadata and any `assetStore.upload(...)` `pluginData`.
|
package/dist/react.d.ts
CHANGED
|
@@ -107,10 +107,18 @@ type IngestAssetFilesToSceneItemsOptions = {
|
|
|
107
107
|
/**
|
|
108
108
|
* PDF rasterization scale forwarded to `pdfjs-dist`.
|
|
109
109
|
*
|
|
110
|
-
* Defaults to `1.
|
|
110
|
+
* Defaults to `1.15` to balance first paint latency with zoom quality.
|
|
111
111
|
*/
|
|
112
112
|
pdfScale?: number;
|
|
113
113
|
pdfPageConcurrency?: number;
|
|
114
|
+
/**
|
|
115
|
+
* Called as soon as canvu finishes converting one or more items from the
|
|
116
|
+
* source files.
|
|
117
|
+
*/
|
|
118
|
+
onItemsReady?: (items: VectorSceneItem[], context: {
|
|
119
|
+
file: File;
|
|
120
|
+
kind: VectorViewportAssetKind;
|
|
121
|
+
}) => void;
|
|
114
122
|
/**
|
|
115
123
|
* Final hook to customize each created item after canvu attaches its own
|
|
116
124
|
* image metadata and any `assetStore.upload(...)` `pluginData`.
|
package/dist/react.js
CHANGED
|
@@ -949,10 +949,32 @@ async function runWithConcurrency(items, concurrency, execute) {
|
|
|
949
949
|
async function loadPdfToStore(file, store, options) {
|
|
950
950
|
const scale = options?.scale ?? 1.5;
|
|
951
951
|
const pageConcurrency = options?.pageConcurrency ?? 2;
|
|
952
|
+
const storeThumbnails = options?.storeThumbnails ?? false;
|
|
952
953
|
const pdfjs = await getPdfJs();
|
|
953
954
|
const arrayBuffer = await file.arrayBuffer();
|
|
954
955
|
const pdf = await pdfjs.getDocument({ data: arrayBuffer }).promise;
|
|
955
956
|
const pageNumbers = normalizePdfPageNumbers(options?.pageNumbers, pdf.numPages);
|
|
957
|
+
const bufferedResults = /* @__PURE__ */ new Map();
|
|
958
|
+
let nextEmitIndex = 0;
|
|
959
|
+
let emitChain = Promise.resolve();
|
|
960
|
+
const queuePageEmission = async (pageResult) => {
|
|
961
|
+
bufferedResults.set(pageResult.pageNumber, pageResult);
|
|
962
|
+
const run = async () => {
|
|
963
|
+
while (nextEmitIndex < pageNumbers.length) {
|
|
964
|
+
const nextPageNumber = pageNumbers[nextEmitIndex];
|
|
965
|
+
if (nextPageNumber == null) break;
|
|
966
|
+
const bufferedResult = bufferedResults.get(nextPageNumber);
|
|
967
|
+
if (!bufferedResult) break;
|
|
968
|
+
bufferedResults.delete(nextPageNumber);
|
|
969
|
+
nextEmitIndex += 1;
|
|
970
|
+
await options?.onPageStored?.(bufferedResult);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
const nextChain = emitChain.then(run, run);
|
|
974
|
+
emitChain = nextChain.catch(() => {
|
|
975
|
+
});
|
|
976
|
+
await nextChain;
|
|
977
|
+
};
|
|
956
978
|
return await runWithConcurrency(
|
|
957
979
|
pageNumbers,
|
|
958
980
|
pageConcurrency,
|
|
@@ -962,27 +984,31 @@ async function loadPdfToStore(file, store, options) {
|
|
|
962
984
|
const mime = "image/png";
|
|
963
985
|
const pageBlob = await canvasToBlob2(canvas, mime);
|
|
964
986
|
const blobId = await store.storeOriginal(pageBlob);
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
tCtx
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
987
|
+
const thumbnailBlobId = storeThumbnails ? await (async () => {
|
|
988
|
+
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
989
|
+
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
990
|
+
const th = Math.max(1, Math.round(height * thumbScale));
|
|
991
|
+
const thumbCanvas = document.createElement("canvas");
|
|
992
|
+
thumbCanvas.width = tw;
|
|
993
|
+
thumbCanvas.height = th;
|
|
994
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
995
|
+
if (tCtx) {
|
|
996
|
+
tCtx.imageSmoothingEnabled = true;
|
|
997
|
+
tCtx.imageSmoothingQuality = "high";
|
|
998
|
+
tCtx.drawImage(canvas, 0, 0, tw, th);
|
|
999
|
+
}
|
|
1000
|
+
const thumbBlob = await canvasToBlob2(thumbCanvas, mime);
|
|
1001
|
+
return await store.storeThumbnail(thumbBlob);
|
|
1002
|
+
})() : "";
|
|
1003
|
+
const pageResult = {
|
|
980
1004
|
blobId,
|
|
981
1005
|
thumbnailBlobId,
|
|
982
1006
|
width,
|
|
983
1007
|
height,
|
|
984
1008
|
pageNumber
|
|
985
1009
|
};
|
|
1010
|
+
await queuePageEmission(pageResult);
|
|
1011
|
+
return pageResult;
|
|
986
1012
|
}
|
|
987
1013
|
);
|
|
988
1014
|
}
|
|
@@ -1338,9 +1364,10 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1338
1364
|
createId = createShapeId,
|
|
1339
1365
|
gapWorld = 16,
|
|
1340
1366
|
stepWorld = 48,
|
|
1341
|
-
pdfScale = 1.
|
|
1367
|
+
pdfScale = 1.15,
|
|
1342
1368
|
pdfPageConcurrency = 2,
|
|
1343
1369
|
decorateItem,
|
|
1370
|
+
onItemsReady,
|
|
1344
1371
|
onError
|
|
1345
1372
|
} = options;
|
|
1346
1373
|
const items = [];
|
|
@@ -1362,55 +1389,56 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1362
1389
|
}
|
|
1363
1390
|
try {
|
|
1364
1391
|
if (kind === "pdf") {
|
|
1365
|
-
const
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1392
|
+
const uploadResultPromise = uploadAssetIfNeeded(assetStore, file, kind);
|
|
1393
|
+
await loadPdfToStore(file, imageStore, {
|
|
1394
|
+
scale: pdfScale,
|
|
1395
|
+
pageConcurrency: pdfPageConcurrency,
|
|
1396
|
+
storeThumbnails: false,
|
|
1397
|
+
onPageStored: async (page) => {
|
|
1398
|
+
const uploadResult2 = await uploadResultPromise;
|
|
1399
|
+
const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
|
|
1400
|
+
const naturalTopY2 = worldCenter.y - page.height / 2;
|
|
1401
|
+
const stackedTopY = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
|
|
1402
|
+
const bounds2 = {
|
|
1403
|
+
x: worldCenter.x - page.width / 2,
|
|
1404
|
+
y: Math.max(naturalTopY2, stackedTopY),
|
|
1405
|
+
width: page.width,
|
|
1406
|
+
height: page.height
|
|
1407
|
+
};
|
|
1408
|
+
const itemContext2 = {
|
|
1409
|
+
file,
|
|
1410
|
+
kind,
|
|
1411
|
+
itemIndex: items.length,
|
|
1412
|
+
pageNumber: page.pageNumber
|
|
1413
|
+
};
|
|
1414
|
+
const item2 = finalizeIngestedItem(
|
|
1415
|
+
{
|
|
1416
|
+
id: createId(),
|
|
1417
|
+
x: bounds2.x,
|
|
1418
|
+
y: bounds2.y,
|
|
1419
|
+
bounds: { ...bounds2 },
|
|
1420
|
+
toolKind: "image",
|
|
1421
|
+
imageBlobId: page.blobId,
|
|
1422
|
+
imageRasterHref: fullUrl2 ?? void 0,
|
|
1423
|
+
imageIntrinsicSize: {
|
|
1424
|
+
width: page.width,
|
|
1425
|
+
height: page.height
|
|
1426
|
+
},
|
|
1427
|
+
childrenSvg: fullUrl2 ? buildRasterImageChildrenSvg(
|
|
1428
|
+
fullUrl2,
|
|
1429
|
+
{ width: page.width, height: page.height },
|
|
1430
|
+
bounds2
|
|
1431
|
+
) : ""
|
|
1400
1432
|
},
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
);
|
|
1411
|
-
items.push(item2);
|
|
1412
|
-
occupiedBottomY = bounds2.y + page.height;
|
|
1413
|
-
}
|
|
1433
|
+
itemContext2,
|
|
1434
|
+
uploadResult2,
|
|
1435
|
+
decorateItem
|
|
1436
|
+
);
|
|
1437
|
+
items.push(item2);
|
|
1438
|
+
occupiedBottomY = bounds2.y + page.height;
|
|
1439
|
+
onItemsReady?.([item2], { file, kind });
|
|
1440
|
+
}
|
|
1441
|
+
});
|
|
1414
1442
|
hasImagePlacementBase = false;
|
|
1415
1443
|
imagePlacementIndex = 0;
|
|
1416
1444
|
imageYOffsetAdjustment = 0;
|
|
@@ -1463,6 +1491,7 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1463
1491
|
decorateItem
|
|
1464
1492
|
);
|
|
1465
1493
|
items.push(item);
|
|
1494
|
+
onItemsReady?.([item], { file, kind });
|
|
1466
1495
|
imagePlacementIndex++;
|
|
1467
1496
|
occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
|
|
1468
1497
|
} catch (error) {
|
|
@@ -7375,7 +7404,13 @@ var VectorViewport = forwardRef(
|
|
|
7375
7404
|
y: worldY
|
|
7376
7405
|
},
|
|
7377
7406
|
imageStore: store,
|
|
7378
|
-
assetStore: assetStoreRef.current ?? void 0
|
|
7407
|
+
assetStore: assetStoreRef.current ?? void 0,
|
|
7408
|
+
onItemsReady: (nextItems) => {
|
|
7409
|
+
if (nextItems.length === 0) return;
|
|
7410
|
+
setLoadingSkeletons([]);
|
|
7411
|
+
change([...itemsRef.current, ...nextItems]);
|
|
7412
|
+
setEffectiveSelectedIdsRef.current(nextItems.map((item) => item.id));
|
|
7413
|
+
}
|
|
7379
7414
|
});
|
|
7380
7415
|
if (result.errors.length > 0) {
|
|
7381
7416
|
for (const error of result.errors) {
|
|
@@ -7383,8 +7418,6 @@ var VectorViewport = forwardRef(
|
|
|
7383
7418
|
}
|
|
7384
7419
|
}
|
|
7385
7420
|
if (result.items.length === 0) return;
|
|
7386
|
-
change([...itemsRef.current, ...result.items]);
|
|
7387
|
-
setEffectiveSelectedIdsRef.current(result.items.map((item) => item.id));
|
|
7388
7421
|
} finally {
|
|
7389
7422
|
setLoadingSkeletons([]);
|
|
7390
7423
|
}
|