canvu-react 0.4.73 → 0.4.75
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 +402 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +402 -25
- package/dist/index.js.map +1 -1
- package/dist/pdf.mjs +28 -0
- package/dist/pdf.worker.mjs +31 -0
- package/dist/react.cjs +490 -40
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +490 -40
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
package/dist/react.js
CHANGED
|
@@ -1186,6 +1186,342 @@ async function createThumbnailBlobUrlFromStore(store, thumbnailBlobId) {
|
|
|
1186
1186
|
return URL.createObjectURL(blob);
|
|
1187
1187
|
}
|
|
1188
1188
|
|
|
1189
|
+
// src/image/pdf-worker-renderer.ts
|
|
1190
|
+
var PDF_WORKER_SOURCE = `
|
|
1191
|
+
let pdfjsPromise = null;
|
|
1192
|
+
|
|
1193
|
+
const loadPdfJs = async (pdfjsModuleCandidates) => {
|
|
1194
|
+
if (!pdfjsPromise) {
|
|
1195
|
+
pdfjsPromise = (async () => {
|
|
1196
|
+
let lastError;
|
|
1197
|
+
for (const candidate of pdfjsModuleCandidates) {
|
|
1198
|
+
try {
|
|
1199
|
+
const pdfjs = await import(candidate.moduleUrl);
|
|
1200
|
+
if (pdfjs.GlobalWorkerOptions) {
|
|
1201
|
+
pdfjs.GlobalWorkerOptions.workerSrc = candidate.workerUrl;
|
|
1202
|
+
}
|
|
1203
|
+
return pdfjs;
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
lastError = error;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
throw lastError ?? new Error("Unable to load pdfjs-dist");
|
|
1209
|
+
})();
|
|
1210
|
+
}
|
|
1211
|
+
return await pdfjsPromise;
|
|
1212
|
+
};
|
|
1213
|
+
|
|
1214
|
+
const createErrorPayload = (error) => ({
|
|
1215
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1216
|
+
name: error instanceof Error ? error.name : "Error",
|
|
1217
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
const normalizePdfPageNumbers = (pageNumbers, pageCount) => {
|
|
1221
|
+
if (!Array.isArray(pageNumbers) || pageNumbers.length === 0) {
|
|
1222
|
+
return Array.from({ length: pageCount }, (_, index) => index + 1);
|
|
1223
|
+
}
|
|
1224
|
+
return [...new Set(pageNumbers)]
|
|
1225
|
+
.filter((pageNumber) => pageNumber >= 1 && pageNumber <= pageCount)
|
|
1226
|
+
.sort((left, right) => left - right);
|
|
1227
|
+
};
|
|
1228
|
+
|
|
1229
|
+
const runWithConcurrency = async (items, concurrency, execute) => {
|
|
1230
|
+
const results = new Array(items.length);
|
|
1231
|
+
let nextIndex = 0;
|
|
1232
|
+
const safeConcurrency = Number.isFinite(concurrency) && concurrency > 0
|
|
1233
|
+
? Math.round(concurrency)
|
|
1234
|
+
: 1;
|
|
1235
|
+
const workerCount = Math.max(1, Math.min(safeConcurrency, items.length));
|
|
1236
|
+
await Promise.all(
|
|
1237
|
+
Array.from({ length: workerCount }, async () => {
|
|
1238
|
+
while (nextIndex < items.length) {
|
|
1239
|
+
const currentIndex = nextIndex;
|
|
1240
|
+
nextIndex += 1;
|
|
1241
|
+
const item = items[currentIndex];
|
|
1242
|
+
if (item === undefined) {
|
|
1243
|
+
continue;
|
|
1244
|
+
}
|
|
1245
|
+
results[currentIndex] = await execute(item);
|
|
1246
|
+
}
|
|
1247
|
+
}),
|
|
1248
|
+
);
|
|
1249
|
+
return results;
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1252
|
+
const renderPageToBlob = async (page, scale, storeThumbnails) => {
|
|
1253
|
+
const raw = page.getViewport({ scale: 1 });
|
|
1254
|
+
const adjustedScale = Math.round(raw.width * scale) / raw.width;
|
|
1255
|
+
const viewport = page.getViewport({ scale: adjustedScale });
|
|
1256
|
+
const width = Math.round(viewport.width);
|
|
1257
|
+
const height = Math.round(viewport.height);
|
|
1258
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
1259
|
+
const canvasContext = canvas.getContext("2d");
|
|
1260
|
+
if (!canvasContext) {
|
|
1261
|
+
throw new Error("OffscreenCanvas 2D context unavailable");
|
|
1262
|
+
}
|
|
1263
|
+
canvasContext.imageSmoothingEnabled = true;
|
|
1264
|
+
canvasContext.imageSmoothingQuality = "high";
|
|
1265
|
+
await page.render({ canvasContext, viewport }).promise;
|
|
1266
|
+
const blob = await canvas.convertToBlob({ type: "image/png" });
|
|
1267
|
+
let thumbnailBlob;
|
|
1268
|
+
if (storeThumbnails) {
|
|
1269
|
+
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
1270
|
+
const thumbnailWidth = Math.max(1, Math.round(width * thumbScale));
|
|
1271
|
+
const thumbnailHeight = Math.max(1, Math.round(height * thumbScale));
|
|
1272
|
+
const thumbnailCanvas = new OffscreenCanvas(thumbnailWidth, thumbnailHeight);
|
|
1273
|
+
const thumbnailContext = thumbnailCanvas.getContext("2d");
|
|
1274
|
+
if (thumbnailContext) {
|
|
1275
|
+
thumbnailContext.imageSmoothingEnabled = true;
|
|
1276
|
+
thumbnailContext.imageSmoothingQuality = "high";
|
|
1277
|
+
thumbnailContext.drawImage(canvas, 0, 0, thumbnailWidth, thumbnailHeight);
|
|
1278
|
+
}
|
|
1279
|
+
thumbnailBlob = await thumbnailCanvas.convertToBlob({ type: "image/png" });
|
|
1280
|
+
}
|
|
1281
|
+
return { blob, height, thumbnailBlob, width };
|
|
1282
|
+
};
|
|
1283
|
+
|
|
1284
|
+
self.onmessage = async (event) => {
|
|
1285
|
+
const message = event.data;
|
|
1286
|
+
if (!message || message.type !== "render") {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
try {
|
|
1290
|
+
const pdfjs = await loadPdfJs(message.pdfjsModuleCandidates);
|
|
1291
|
+
const pdf = await pdfjs.getDocument({
|
|
1292
|
+
data: new Uint8Array(message.pdfData),
|
|
1293
|
+
disableWorker: true,
|
|
1294
|
+
}).promise;
|
|
1295
|
+
try {
|
|
1296
|
+
const pageNumbers = normalizePdfPageNumbers(message.pageNumbers, pdf.numPages);
|
|
1297
|
+
const bufferedPages = new Map();
|
|
1298
|
+
let nextEmitIndex = 0;
|
|
1299
|
+
let emitChain = Promise.resolve();
|
|
1300
|
+
const emitPageInOrder = async (pageNumber, renderedPage) => {
|
|
1301
|
+
bufferedPages.set(pageNumber, renderedPage);
|
|
1302
|
+
const flush = () => {
|
|
1303
|
+
while (nextEmitIndex < pageNumbers.length) {
|
|
1304
|
+
const nextPageNumber = pageNumbers[nextEmitIndex];
|
|
1305
|
+
if (nextPageNumber == null) break;
|
|
1306
|
+
const nextPage = bufferedPages.get(nextPageNumber);
|
|
1307
|
+
if (!nextPage) break;
|
|
1308
|
+
bufferedPages.delete(nextPageNumber);
|
|
1309
|
+
nextEmitIndex += 1;
|
|
1310
|
+
self.postMessage({
|
|
1311
|
+
type: "page",
|
|
1312
|
+
page: {
|
|
1313
|
+
...nextPage,
|
|
1314
|
+
pageNumber: nextPageNumber,
|
|
1315
|
+
},
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
};
|
|
1319
|
+
const nextChain = emitChain.then(flush, flush);
|
|
1320
|
+
emitChain = nextChain.catch(() => {});
|
|
1321
|
+
await nextChain;
|
|
1322
|
+
};
|
|
1323
|
+
await runWithConcurrency(
|
|
1324
|
+
pageNumbers,
|
|
1325
|
+
message.pageConcurrency,
|
|
1326
|
+
async (pageNumber) => {
|
|
1327
|
+
const page = await pdf.getPage(pageNumber);
|
|
1328
|
+
try {
|
|
1329
|
+
const renderedPage = await renderPageToBlob(
|
|
1330
|
+
page,
|
|
1331
|
+
message.scale,
|
|
1332
|
+
message.storeThumbnails,
|
|
1333
|
+
);
|
|
1334
|
+
await emitPageInOrder(pageNumber, renderedPage);
|
|
1335
|
+
} finally {
|
|
1336
|
+
page.cleanup();
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
);
|
|
1340
|
+
await emitChain;
|
|
1341
|
+
} finally {
|
|
1342
|
+
await pdf.destroy();
|
|
1343
|
+
}
|
|
1344
|
+
self.postMessage({ type: "done" });
|
|
1345
|
+
} catch (error) {
|
|
1346
|
+
self.postMessage({ type: "error", error: createErrorPayload(error) });
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
`;
|
|
1350
|
+
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
1351
|
+
var hasObjectMembers = (value) => (typeof value === "object" || typeof value === "function") && value !== null;
|
|
1352
|
+
var isOffscreenCanvasCandidate = (value) => typeof value === "function";
|
|
1353
|
+
var getEnvironmentRecord = (environment) => isRecord(environment) ? environment : null;
|
|
1354
|
+
var canUsePdfWorkerRenderer = (environment = globalThis) => {
|
|
1355
|
+
const candidateEnvironment = getEnvironmentRecord(environment);
|
|
1356
|
+
if (!candidateEnvironment) return false;
|
|
1357
|
+
if (typeof candidateEnvironment.Worker !== "function") return false;
|
|
1358
|
+
if (typeof candidateEnvironment.Blob !== "function") return false;
|
|
1359
|
+
if (!isOffscreenCanvasCandidate(candidateEnvironment.OffscreenCanvas)) {
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
const urlCandidate = candidateEnvironment.URL;
|
|
1363
|
+
if (!hasObjectMembers(urlCandidate)) return false;
|
|
1364
|
+
if (typeof urlCandidate.createObjectURL !== "function") return false;
|
|
1365
|
+
if (typeof urlCandidate.revokeObjectURL !== "function") return false;
|
|
1366
|
+
try {
|
|
1367
|
+
const canvas = new candidateEnvironment.OffscreenCanvas(1, 1);
|
|
1368
|
+
return typeof canvas.getContext === "function" && typeof canvas.convertToBlob === "function";
|
|
1369
|
+
} catch {
|
|
1370
|
+
return false;
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
var createAbortError = () => new DOMException("Aborted", "AbortError");
|
|
1374
|
+
var throwIfAborted = (signal) => {
|
|
1375
|
+
if (signal?.aborted) {
|
|
1376
|
+
throw createAbortError();
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
var createPdfWorker = () => {
|
|
1380
|
+
const workerBlob = new Blob([PDF_WORKER_SOURCE], {
|
|
1381
|
+
type: "text/javascript"
|
|
1382
|
+
});
|
|
1383
|
+
const workerUrl = URL.createObjectURL(workerBlob);
|
|
1384
|
+
try {
|
|
1385
|
+
const worker = new Worker(workerUrl, {
|
|
1386
|
+
name: "canvu-pdf-renderer",
|
|
1387
|
+
type: "module"
|
|
1388
|
+
});
|
|
1389
|
+
return {
|
|
1390
|
+
release: () => URL.revokeObjectURL(workerUrl),
|
|
1391
|
+
worker
|
|
1392
|
+
};
|
|
1393
|
+
} catch (error) {
|
|
1394
|
+
URL.revokeObjectURL(workerUrl);
|
|
1395
|
+
throw error;
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1398
|
+
var createWorkerError = (message) => {
|
|
1399
|
+
const error = new Error(message.error.message);
|
|
1400
|
+
error.name = message.error.name;
|
|
1401
|
+
error.stack = message.error.stack;
|
|
1402
|
+
return error;
|
|
1403
|
+
};
|
|
1404
|
+
var PACKAGED_PDFJS_MODULE_FILE = "./pdf.mjs";
|
|
1405
|
+
var PACKAGED_PDFJS_WORKER_FILE = "./pdf.worker.mjs";
|
|
1406
|
+
var resolveRelativeAssetUrl = (path, baseUrl) => new URL(path, baseUrl).toString();
|
|
1407
|
+
var resolvePackagedPdfJsModuleCandidate = (baseUrl = import.meta.url) => ({
|
|
1408
|
+
moduleUrl: resolveRelativeAssetUrl(PACKAGED_PDFJS_MODULE_FILE, baseUrl),
|
|
1409
|
+
workerUrl: resolveRelativeAssetUrl(PACKAGED_PDFJS_WORKER_FILE, baseUrl)
|
|
1410
|
+
});
|
|
1411
|
+
var resolveBundledPdfJsModuleCandidate = () => ({
|
|
1412
|
+
moduleUrl: new URL("pdfjs-dist/build/pdf.min.mjs", import.meta.url).toString(),
|
|
1413
|
+
workerUrl: new URL(
|
|
1414
|
+
"pdfjs-dist/build/pdf.worker.min.mjs",
|
|
1415
|
+
import.meta.url
|
|
1416
|
+
).toString()
|
|
1417
|
+
});
|
|
1418
|
+
var resolvePdfJsModuleCandidates = () => [
|
|
1419
|
+
resolvePackagedPdfJsModuleCandidate(),
|
|
1420
|
+
resolveBundledPdfJsModuleCandidate()
|
|
1421
|
+
];
|
|
1422
|
+
var loadPdfToStoreWithWorker = async (file, store, options) => {
|
|
1423
|
+
throwIfAborted(options.signal);
|
|
1424
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
1425
|
+
throwIfAborted(options.signal);
|
|
1426
|
+
return await new Promise((resolve, reject) => {
|
|
1427
|
+
const { release, worker } = createPdfWorker();
|
|
1428
|
+
const pageResults = [];
|
|
1429
|
+
let storeChain = Promise.resolve();
|
|
1430
|
+
let cleanedUp = false;
|
|
1431
|
+
let settled = false;
|
|
1432
|
+
const cleanup = () => {
|
|
1433
|
+
if (cleanedUp) return;
|
|
1434
|
+
cleanedUp = true;
|
|
1435
|
+
options.signal?.removeEventListener("abort", abortWorker);
|
|
1436
|
+
worker.onmessage = null;
|
|
1437
|
+
worker.onerror = null;
|
|
1438
|
+
worker.terminate();
|
|
1439
|
+
release();
|
|
1440
|
+
};
|
|
1441
|
+
const rejectOnce = (error) => {
|
|
1442
|
+
if (settled) return;
|
|
1443
|
+
settled = true;
|
|
1444
|
+
cleanup();
|
|
1445
|
+
reject(error);
|
|
1446
|
+
};
|
|
1447
|
+
const rejectAfterQueuedStores = (error) => {
|
|
1448
|
+
if (settled) return;
|
|
1449
|
+
cleanup();
|
|
1450
|
+
storeChain.then(() => {
|
|
1451
|
+
if (settled) return;
|
|
1452
|
+
settled = true;
|
|
1453
|
+
reject(error);
|
|
1454
|
+
}).catch(rejectOnce);
|
|
1455
|
+
};
|
|
1456
|
+
function abortWorker() {
|
|
1457
|
+
rejectOnce(createAbortError());
|
|
1458
|
+
}
|
|
1459
|
+
const storePage = (message) => {
|
|
1460
|
+
try {
|
|
1461
|
+
options.onPageReceived?.({ pageNumber: message.page.pageNumber });
|
|
1462
|
+
} catch (error) {
|
|
1463
|
+
rejectOnce(error);
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
storeChain = storeChain.then(async () => {
|
|
1467
|
+
throwIfAborted(options.signal);
|
|
1468
|
+
const blobId = await store.storeOriginal(message.page.blob);
|
|
1469
|
+
throwIfAborted(options.signal);
|
|
1470
|
+
const thumbnailBlobId = options.storeThumbnails && message.page.thumbnailBlob ? await store.storeThumbnail(message.page.thumbnailBlob) : "";
|
|
1471
|
+
throwIfAborted(options.signal);
|
|
1472
|
+
const pageResult = {
|
|
1473
|
+
blobId,
|
|
1474
|
+
height: message.page.height,
|
|
1475
|
+
pageNumber: message.page.pageNumber,
|
|
1476
|
+
thumbnailBlobId,
|
|
1477
|
+
width: message.page.width
|
|
1478
|
+
};
|
|
1479
|
+
pageResults.push(pageResult);
|
|
1480
|
+
await options.onPageStored?.(pageResult);
|
|
1481
|
+
throwIfAborted(options.signal);
|
|
1482
|
+
});
|
|
1483
|
+
void storeChain.catch(rejectOnce);
|
|
1484
|
+
};
|
|
1485
|
+
worker.onmessage = (event) => {
|
|
1486
|
+
const message = event.data;
|
|
1487
|
+
if (message.type === "page") {
|
|
1488
|
+
storePage(message);
|
|
1489
|
+
return;
|
|
1490
|
+
}
|
|
1491
|
+
if (message.type === "error") {
|
|
1492
|
+
rejectAfterQueuedStores(createWorkerError(message));
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
storeChain.then(() => {
|
|
1496
|
+
if (settled) return;
|
|
1497
|
+
settled = true;
|
|
1498
|
+
cleanup();
|
|
1499
|
+
resolve(pageResults);
|
|
1500
|
+
}).catch(rejectOnce);
|
|
1501
|
+
};
|
|
1502
|
+
worker.onerror = (event) => {
|
|
1503
|
+
rejectAfterQueuedStores(event.error ?? new Error(event.message));
|
|
1504
|
+
};
|
|
1505
|
+
options.signal?.addEventListener("abort", abortWorker, { once: true });
|
|
1506
|
+
try {
|
|
1507
|
+
worker.postMessage(
|
|
1508
|
+
{
|
|
1509
|
+
pdfData: arrayBuffer,
|
|
1510
|
+
pdfjsModuleCandidates: resolvePdfJsModuleCandidates(),
|
|
1511
|
+
pageNumbers: options.pageNumbers ? [...options.pageNumbers] : void 0,
|
|
1512
|
+
pageConcurrency: options.pageConcurrency,
|
|
1513
|
+
scale: options.scale,
|
|
1514
|
+
storeThumbnails: options.storeThumbnails,
|
|
1515
|
+
type: "render"
|
|
1516
|
+
},
|
|
1517
|
+
[arrayBuffer]
|
|
1518
|
+
);
|
|
1519
|
+
} catch (error) {
|
|
1520
|
+
rejectOnce(error);
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
};
|
|
1524
|
+
|
|
1189
1525
|
// src/image/pdf-loader.ts
|
|
1190
1526
|
var pdfjsPromise = null;
|
|
1191
1527
|
function getPdfJs() {
|
|
@@ -1202,7 +1538,7 @@ function getPdfJs() {
|
|
|
1202
1538
|
return pdfjsPromise;
|
|
1203
1539
|
}
|
|
1204
1540
|
async function renderPageToCanvas(page, scale, signal) {
|
|
1205
|
-
|
|
1541
|
+
throwIfAborted2(signal);
|
|
1206
1542
|
const raw = page.getViewport({ scale: 1 });
|
|
1207
1543
|
const adjustedScale = Math.round(raw.width * scale) / raw.width;
|
|
1208
1544
|
const viewport = page.getViewport({ scale: adjustedScale });
|
|
@@ -1224,7 +1560,7 @@ async function renderPageToCanvas(page, scale, signal) {
|
|
|
1224
1560
|
try {
|
|
1225
1561
|
await renderTask.promise;
|
|
1226
1562
|
} catch (error) {
|
|
1227
|
-
if (signal.aborted) throw
|
|
1563
|
+
if (signal.aborted) throw createAbortError2();
|
|
1228
1564
|
throw error;
|
|
1229
1565
|
} finally {
|
|
1230
1566
|
signal.removeEventListener("abort", abortRender);
|
|
@@ -1232,15 +1568,15 @@ async function renderPageToCanvas(page, scale, signal) {
|
|
|
1232
1568
|
} else {
|
|
1233
1569
|
await renderTask.promise;
|
|
1234
1570
|
}
|
|
1235
|
-
|
|
1571
|
+
throwIfAborted2(signal);
|
|
1236
1572
|
return { canvas, width: w, height: h };
|
|
1237
1573
|
}
|
|
1238
|
-
function
|
|
1574
|
+
function createAbortError2() {
|
|
1239
1575
|
return new DOMException("Aborted", "AbortError");
|
|
1240
1576
|
}
|
|
1241
|
-
function
|
|
1577
|
+
function throwIfAborted2(signal) {
|
|
1242
1578
|
if (signal?.aborted) {
|
|
1243
|
-
throw
|
|
1579
|
+
throw createAbortError2();
|
|
1244
1580
|
}
|
|
1245
1581
|
}
|
|
1246
1582
|
function normalizePdfPageNumbers(pageNumbers, pageCount) {
|
|
@@ -1256,7 +1592,7 @@ async function runWithConcurrency(items, concurrency, execute, signal) {
|
|
|
1256
1592
|
await Promise.all(
|
|
1257
1593
|
Array.from({ length: workerCount }, async () => {
|
|
1258
1594
|
while (nextIndex < items.length) {
|
|
1259
|
-
|
|
1595
|
+
throwIfAborted2(signal);
|
|
1260
1596
|
const currentIndex = nextIndex;
|
|
1261
1597
|
nextIndex += 1;
|
|
1262
1598
|
const item = items[currentIndex];
|
|
@@ -1264,7 +1600,7 @@ async function runWithConcurrency(items, concurrency, execute, signal) {
|
|
|
1264
1600
|
continue;
|
|
1265
1601
|
}
|
|
1266
1602
|
results[currentIndex] = await execute(item);
|
|
1267
|
-
|
|
1603
|
+
throwIfAborted2(signal);
|
|
1268
1604
|
}
|
|
1269
1605
|
})
|
|
1270
1606
|
);
|
|
@@ -1275,11 +1611,52 @@ async function loadPdfToStore(file, store, options) {
|
|
|
1275
1611
|
const pageConcurrency = options?.pageConcurrency ?? 2;
|
|
1276
1612
|
const storeThumbnails = options?.storeThumbnails ?? false;
|
|
1277
1613
|
const signal = options?.signal;
|
|
1278
|
-
|
|
1614
|
+
throwIfAborted2(signal);
|
|
1615
|
+
if (canUsePdfWorkerRenderer()) {
|
|
1616
|
+
let workerPageCount = 0;
|
|
1617
|
+
try {
|
|
1618
|
+
return await loadPdfToStoreWithWorker(file, store, {
|
|
1619
|
+
scale,
|
|
1620
|
+
pageNumbers: options?.pageNumbers,
|
|
1621
|
+
pageConcurrency,
|
|
1622
|
+
storeThumbnails,
|
|
1623
|
+
signal,
|
|
1624
|
+
onPageReceived: () => {
|
|
1625
|
+
workerPageCount += 1;
|
|
1626
|
+
},
|
|
1627
|
+
onPageStored: async (page) => {
|
|
1628
|
+
await options?.onPageStored?.(page);
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
} catch (error) {
|
|
1632
|
+
if (signal?.aborted || workerPageCount > 0) {
|
|
1633
|
+
throw error;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
return await loadPdfToStoreOnMainThread(file, store, {
|
|
1638
|
+
scale,
|
|
1639
|
+
pageNumbers: options?.pageNumbers,
|
|
1640
|
+
pageConcurrency,
|
|
1641
|
+
storeThumbnails,
|
|
1642
|
+
signal,
|
|
1643
|
+
onPageStored: options?.onPageStored
|
|
1644
|
+
});
|
|
1645
|
+
}
|
|
1646
|
+
async function loadPdfToStoreOnMainThread(file, store, options) {
|
|
1647
|
+
const {
|
|
1648
|
+
pageConcurrency,
|
|
1649
|
+
scale,
|
|
1650
|
+
signal,
|
|
1651
|
+
storeThumbnails,
|
|
1652
|
+
onPageStored,
|
|
1653
|
+
pageNumbers
|
|
1654
|
+
} = options;
|
|
1655
|
+
throwIfAborted2(signal);
|
|
1279
1656
|
const pdfjs = await getPdfJs();
|
|
1280
|
-
|
|
1657
|
+
throwIfAborted2(signal);
|
|
1281
1658
|
const arrayBuffer = await file.arrayBuffer();
|
|
1282
|
-
|
|
1659
|
+
throwIfAborted2(signal);
|
|
1283
1660
|
const loadingTask = pdfjs.getDocument({ data: arrayBuffer });
|
|
1284
1661
|
if (signal) {
|
|
1285
1662
|
const abortLoading = () => {
|
|
@@ -1291,14 +1668,14 @@ async function loadPdfToStore(file, store, options) {
|
|
|
1291
1668
|
signal.removeEventListener("abort", abortLoading);
|
|
1292
1669
|
return await loadPdfDocumentToStore(pdf2, store, {
|
|
1293
1670
|
scale,
|
|
1294
|
-
pageNumbers
|
|
1671
|
+
pageNumbers,
|
|
1295
1672
|
pageConcurrency,
|
|
1296
1673
|
storeThumbnails,
|
|
1297
1674
|
signal,
|
|
1298
|
-
onPageStored
|
|
1675
|
+
onPageStored
|
|
1299
1676
|
});
|
|
1300
1677
|
} catch (error) {
|
|
1301
|
-
if (signal.aborted) throw
|
|
1678
|
+
if (signal.aborted) throw createAbortError2();
|
|
1302
1679
|
throw error;
|
|
1303
1680
|
} finally {
|
|
1304
1681
|
signal.removeEventListener("abort", abortLoading);
|
|
@@ -1307,16 +1684,16 @@ async function loadPdfToStore(file, store, options) {
|
|
|
1307
1684
|
const pdf = await loadingTask.promise;
|
|
1308
1685
|
return await loadPdfDocumentToStore(pdf, store, {
|
|
1309
1686
|
scale,
|
|
1310
|
-
pageNumbers
|
|
1687
|
+
pageNumbers,
|
|
1311
1688
|
pageConcurrency,
|
|
1312
1689
|
storeThumbnails,
|
|
1313
|
-
onPageStored
|
|
1690
|
+
onPageStored
|
|
1314
1691
|
});
|
|
1315
1692
|
}
|
|
1316
1693
|
async function loadPdfDocumentToStore(pdf, store, options) {
|
|
1317
1694
|
const { pageConcurrency, scale, signal } = options;
|
|
1318
1695
|
const storeThumbnails = options.storeThumbnails ?? false;
|
|
1319
|
-
|
|
1696
|
+
throwIfAborted2(signal);
|
|
1320
1697
|
const pageNumbers = normalizePdfPageNumbers(options?.pageNumbers, pdf.numPages);
|
|
1321
1698
|
const bufferedResults = /* @__PURE__ */ new Map();
|
|
1322
1699
|
let nextEmitIndex = 0;
|
|
@@ -1331,9 +1708,9 @@ async function loadPdfDocumentToStore(pdf, store, options) {
|
|
|
1331
1708
|
if (!bufferedResult) break;
|
|
1332
1709
|
bufferedResults.delete(nextPageNumber);
|
|
1333
1710
|
nextEmitIndex += 1;
|
|
1334
|
-
|
|
1711
|
+
throwIfAborted2(signal);
|
|
1335
1712
|
await options?.onPageStored?.(bufferedResult);
|
|
1336
|
-
|
|
1713
|
+
throwIfAborted2(signal);
|
|
1337
1714
|
}
|
|
1338
1715
|
};
|
|
1339
1716
|
const nextChain = emitChain.then(run, run);
|
|
@@ -1345,20 +1722,20 @@ async function loadPdfDocumentToStore(pdf, store, options) {
|
|
|
1345
1722
|
pageNumbers,
|
|
1346
1723
|
pageConcurrency,
|
|
1347
1724
|
async (pageNumber) => {
|
|
1348
|
-
|
|
1725
|
+
throwIfAborted2(signal);
|
|
1349
1726
|
const page = await pdf.getPage(pageNumber);
|
|
1350
|
-
|
|
1727
|
+
throwIfAborted2(signal);
|
|
1351
1728
|
const { canvas, width, height } = await renderPageToCanvas(
|
|
1352
1729
|
page,
|
|
1353
1730
|
scale,
|
|
1354
1731
|
signal
|
|
1355
1732
|
);
|
|
1356
|
-
|
|
1733
|
+
throwIfAborted2(signal);
|
|
1357
1734
|
const mime = "image/png";
|
|
1358
1735
|
const pageBlob = await encodeCanvasToBlob(canvas, { mimeType: mime });
|
|
1359
|
-
|
|
1736
|
+
throwIfAborted2(signal);
|
|
1360
1737
|
const blobId = await store.storeOriginal(pageBlob);
|
|
1361
|
-
|
|
1738
|
+
throwIfAborted2(signal);
|
|
1362
1739
|
const thumbnailBlobId = storeThumbnails ? await (async () => {
|
|
1363
1740
|
const thumbScale = Math.min(1, 256 / Math.max(width, height));
|
|
1364
1741
|
const tw = Math.max(1, Math.round(width * thumbScale));
|
|
@@ -1375,7 +1752,7 @@ async function loadPdfDocumentToStore(pdf, store, options) {
|
|
|
1375
1752
|
const thumbBlob = await encodeCanvasToBlob(thumbCanvas, {
|
|
1376
1753
|
mimeType: mime
|
|
1377
1754
|
});
|
|
1378
|
-
|
|
1755
|
+
throwIfAborted2(signal);
|
|
1379
1756
|
return await store.storeThumbnail(thumbBlob);
|
|
1380
1757
|
})() : "";
|
|
1381
1758
|
const pageResult = {
|
|
@@ -1729,22 +2106,22 @@ function finalizeIngestedItem(item, context, uploadResult, decorateItem) {
|
|
|
1729
2106
|
const itemWithAssetData = applyAssetUploadResultToItem(item, uploadResult);
|
|
1730
2107
|
return decorateItem ? decorateItem(itemWithAssetData, context) : itemWithAssetData;
|
|
1731
2108
|
}
|
|
1732
|
-
function
|
|
2109
|
+
function createAbortError3() {
|
|
1733
2110
|
return new DOMException("Aborted", "AbortError");
|
|
1734
2111
|
}
|
|
1735
|
-
function
|
|
2112
|
+
function throwIfAborted3(signal) {
|
|
1736
2113
|
if (signal?.aborted) {
|
|
1737
|
-
throw
|
|
2114
|
+
throw createAbortError3();
|
|
1738
2115
|
}
|
|
1739
2116
|
}
|
|
1740
2117
|
function isAbortError(error) {
|
|
1741
2118
|
return (error instanceof DOMException || error instanceof Error) && error.name === "AbortError";
|
|
1742
2119
|
}
|
|
1743
2120
|
async function uploadAssetIfNeeded(assetStore, file, kind, signal) {
|
|
1744
|
-
|
|
2121
|
+
throwIfAborted3(signal);
|
|
1745
2122
|
if (!assetStore) return null;
|
|
1746
2123
|
const result = await assetStore.upload({ file, kind, signal });
|
|
1747
|
-
|
|
2124
|
+
throwIfAborted3(signal);
|
|
1748
2125
|
return result ?? null;
|
|
1749
2126
|
}
|
|
1750
2127
|
async function ingestAssetFilesToSceneItems(options) {
|
|
@@ -1767,7 +2144,7 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1767
2144
|
const errors = [];
|
|
1768
2145
|
let occupiedBottomY = existingItems.length > 0 ? Math.max(...existingItems.map((item) => item.bounds.y + item.bounds.height)) : null;
|
|
1769
2146
|
for (const file of files) {
|
|
1770
|
-
|
|
2147
|
+
throwIfAborted3(signal);
|
|
1771
2148
|
const kind = getAssetKindForFile(file);
|
|
1772
2149
|
if (!kind) {
|
|
1773
2150
|
const error = {
|
|
@@ -1793,10 +2170,10 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1793
2170
|
storeThumbnails: false,
|
|
1794
2171
|
signal,
|
|
1795
2172
|
onPageStored: async (page) => {
|
|
1796
|
-
|
|
2173
|
+
throwIfAborted3(signal);
|
|
1797
2174
|
const uploadResult2 = await uploadResultPromise;
|
|
1798
2175
|
const fullUrl2 = await createBlobUrlFromStore(imageStore, page.blobId);
|
|
1799
|
-
|
|
2176
|
+
throwIfAborted3(signal);
|
|
1800
2177
|
const naturalTopY2 = worldCenter.y - page.height / 2;
|
|
1801
2178
|
const stackedTopY2 = occupiedBottomY == null ? naturalTopY2 : occupiedBottomY + gapWorld;
|
|
1802
2179
|
const bounds2 = {
|
|
@@ -1836,18 +2213,18 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1836
2213
|
);
|
|
1837
2214
|
items.push(item2);
|
|
1838
2215
|
occupiedBottomY = bounds2.y + page.height;
|
|
1839
|
-
|
|
2216
|
+
throwIfAborted3(signal);
|
|
1840
2217
|
onItemsReady?.([item2], { file, kind });
|
|
1841
2218
|
}
|
|
1842
2219
|
});
|
|
1843
2220
|
continue;
|
|
1844
2221
|
}
|
|
1845
|
-
|
|
2222
|
+
throwIfAborted3(signal);
|
|
1846
2223
|
const [uploadResult, storedImage] = await Promise.all([
|
|
1847
2224
|
uploadAssetIfNeeded(assetStore, file, kind, signal),
|
|
1848
2225
|
loadImageToStore(file, imageStore)
|
|
1849
2226
|
]);
|
|
1850
|
-
|
|
2227
|
+
throwIfAborted3(signal);
|
|
1851
2228
|
const { blobId, thumbnailBlobId, width, height } = storedImage;
|
|
1852
2229
|
const fullUrl = await createBlobUrlFromStore(imageStore, blobId);
|
|
1853
2230
|
const thumbBlob = await imageStore.getThumbnail(thumbnailBlobId);
|
|
@@ -1885,7 +2262,7 @@ async function ingestAssetFilesToSceneItems(options) {
|
|
|
1885
2262
|
decorateItem
|
|
1886
2263
|
);
|
|
1887
2264
|
items.push(item);
|
|
1888
|
-
|
|
2265
|
+
throwIfAborted3(signal);
|
|
1889
2266
|
onItemsReady?.([item], { file, kind });
|
|
1890
2267
|
occupiedBottomY = occupiedBottomY == null ? bounds.y + height : Math.max(occupiedBottomY, bounds.y + height);
|
|
1891
2268
|
} catch (error) {
|
|
@@ -8554,6 +8931,11 @@ function shallowEqualStringArray(a, b) {
|
|
|
8554
8931
|
|
|
8555
8932
|
// src/react/stroke-input.ts
|
|
8556
8933
|
var MAX_INTERPOLATED_POINTS_PER_SAMPLE = 192;
|
|
8934
|
+
var MIN_CURVED_INTERPOLATION_DOT = -0.15;
|
|
8935
|
+
var MAX_STRAIGHT_INTERPOLATION_DOT = 0.985;
|
|
8936
|
+
var CURVED_INTERPOLATION_TENSION = 0.22;
|
|
8937
|
+
var MAX_CURVED_CONTROL_DISTANCE_RATIO = 0.5;
|
|
8938
|
+
var MIN_POINT_DISTANCE_SQUARED = 1e-12;
|
|
8557
8939
|
function getPointerEventSamples(event) {
|
|
8558
8940
|
if (typeof event.getCoalescedEvents !== "function") {
|
|
8559
8941
|
return [event];
|
|
@@ -8568,6 +8950,62 @@ var resolveInterpolatedPressure = (lastPoint, nextPoint, ratio) => {
|
|
|
8568
8950
|
return ratio === 1 ? nextPoint.pressure : void 0;
|
|
8569
8951
|
};
|
|
8570
8952
|
var resolveMaxStepWorld = (maxStepWorld) => Number.isFinite(maxStepWorld) && maxStepWorld > 0 ? maxStepWorld : Number.POSITIVE_INFINITY;
|
|
8953
|
+
var squaredDistance = (firstPoint, secondPoint) => {
|
|
8954
|
+
const deltaX = secondPoint.x - firstPoint.x;
|
|
8955
|
+
const deltaY = secondPoint.y - firstPoint.y;
|
|
8956
|
+
return deltaX * deltaX + deltaY * deltaY;
|
|
8957
|
+
};
|
|
8958
|
+
var resolvePreviousDistinctPoint = (points, lastPoint) => {
|
|
8959
|
+
for (let index = points.length - 2; index >= 0; index--) {
|
|
8960
|
+
const candidatePoint = points[index];
|
|
8961
|
+
if (candidatePoint && squaredDistance(candidatePoint, lastPoint) > MIN_POINT_DISTANCE_SQUARED) {
|
|
8962
|
+
return candidatePoint;
|
|
8963
|
+
}
|
|
8964
|
+
}
|
|
8965
|
+
return void 0;
|
|
8966
|
+
};
|
|
8967
|
+
var shouldUseCurvedInterpolation = (previousPoint, lastPoint, nextPoint) => {
|
|
8968
|
+
if (!previousPoint) return false;
|
|
8969
|
+
const incomingX = lastPoint.x - previousPoint.x;
|
|
8970
|
+
const incomingY = lastPoint.y - previousPoint.y;
|
|
8971
|
+
const outgoingX = nextPoint.x - lastPoint.x;
|
|
8972
|
+
const outgoingY = nextPoint.y - lastPoint.y;
|
|
8973
|
+
const incomingLengthSquared = incomingX * incomingX + incomingY * incomingY;
|
|
8974
|
+
const outgoingLengthSquared = outgoingX * outgoingX + outgoingY * outgoingY;
|
|
8975
|
+
if (incomingLengthSquared <= MIN_POINT_DISTANCE_SQUARED || outgoingLengthSquared <= MIN_POINT_DISTANCE_SQUARED) {
|
|
8976
|
+
return false;
|
|
8977
|
+
}
|
|
8978
|
+
const directionDot = (incomingX * outgoingX + incomingY * outgoingY) / Math.sqrt(incomingLengthSquared * outgoingLengthSquared);
|
|
8979
|
+
return directionDot >= MIN_CURVED_INTERPOLATION_DOT && directionDot <= MAX_STRAIGHT_INTERPOLATION_DOT;
|
|
8980
|
+
};
|
|
8981
|
+
var resolveCurvedControlPoint = (previousPoint, lastPoint, nextPoint, distance) => {
|
|
8982
|
+
const tangentX = nextPoint.x - previousPoint.x;
|
|
8983
|
+
const tangentY = nextPoint.y - previousPoint.y;
|
|
8984
|
+
const tangentLength = Math.sqrt(tangentX * tangentX + tangentY * tangentY);
|
|
8985
|
+
if (tangentLength <= MIN_POINT_DISTANCE_SQUARED) return void 0;
|
|
8986
|
+
const controlDistance = Math.min(
|
|
8987
|
+
distance * MAX_CURVED_CONTROL_DISTANCE_RATIO,
|
|
8988
|
+
tangentLength * CURVED_INTERPOLATION_TENSION
|
|
8989
|
+
);
|
|
8990
|
+
const scale = controlDistance / tangentLength;
|
|
8991
|
+
return {
|
|
8992
|
+
x: lastPoint.x + tangentX * scale,
|
|
8993
|
+
y: lastPoint.y + tangentY * scale
|
|
8994
|
+
};
|
|
8995
|
+
};
|
|
8996
|
+
var resolveInterpolatedPosition = (lastPoint, nextPoint, controlPoint, ratio) => {
|
|
8997
|
+
if (!controlPoint) {
|
|
8998
|
+
return {
|
|
8999
|
+
x: lastPoint.x + (nextPoint.x - lastPoint.x) * ratio,
|
|
9000
|
+
y: lastPoint.y + (nextPoint.y - lastPoint.y) * ratio
|
|
9001
|
+
};
|
|
9002
|
+
}
|
|
9003
|
+
const inverseRatio = 1 - ratio;
|
|
9004
|
+
return {
|
|
9005
|
+
x: inverseRatio * inverseRatio * lastPoint.x + 2 * inverseRatio * ratio * controlPoint.x + ratio * ratio * nextPoint.x,
|
|
9006
|
+
y: inverseRatio * inverseRatio * lastPoint.y + 2 * inverseRatio * ratio * controlPoint.y + ratio * ratio * nextPoint.y
|
|
9007
|
+
};
|
|
9008
|
+
};
|
|
8571
9009
|
function appendInterpolatedStrokePoint(points, nextPoint, maxStepWorld) {
|
|
8572
9010
|
if (points.length === 0) return [nextPoint];
|
|
8573
9011
|
const lastPoint = points[points.length - 1];
|
|
@@ -8575,19 +9013,31 @@ function appendInterpolatedStrokePoint(points, nextPoint, maxStepWorld) {
|
|
|
8575
9013
|
const deltaX = nextPoint.x - lastPoint.x;
|
|
8576
9014
|
const deltaY = nextPoint.y - lastPoint.y;
|
|
8577
9015
|
const distanceSquared = deltaX * deltaX + deltaY * deltaY;
|
|
8578
|
-
if (distanceSquared <
|
|
9016
|
+
if (distanceSquared < MIN_POINT_DISTANCE_SQUARED) return points;
|
|
8579
9017
|
const distance = Math.sqrt(distanceSquared);
|
|
8580
9018
|
const stepCount = Math.min(
|
|
8581
9019
|
MAX_INTERPOLATED_POINTS_PER_SAMPLE,
|
|
8582
9020
|
Math.max(1, Math.ceil(distance / resolveMaxStepWorld(maxStepWorld)))
|
|
8583
9021
|
);
|
|
8584
9022
|
if (stepCount === 1) return [...points, nextPoint];
|
|
9023
|
+
const previousPoint = resolvePreviousDistinctPoint(points, lastPoint);
|
|
9024
|
+
const controlPoint = shouldUseCurvedInterpolation(
|
|
9025
|
+
previousPoint,
|
|
9026
|
+
lastPoint,
|
|
9027
|
+
nextPoint
|
|
9028
|
+
) ? resolveCurvedControlPoint(previousPoint, lastPoint, nextPoint, distance) : void 0;
|
|
8585
9029
|
const interpolatedPoints = Array.from({ length: stepCount }, (_, index) => {
|
|
8586
9030
|
const ratio = (index + 1) / stepCount;
|
|
8587
9031
|
const pressure = resolveInterpolatedPressure(lastPoint, nextPoint, ratio);
|
|
9032
|
+
const position = resolveInterpolatedPosition(
|
|
9033
|
+
lastPoint,
|
|
9034
|
+
nextPoint,
|
|
9035
|
+
controlPoint,
|
|
9036
|
+
ratio
|
|
9037
|
+
);
|
|
8588
9038
|
return {
|
|
8589
|
-
x:
|
|
8590
|
-
y:
|
|
9039
|
+
x: position.x,
|
|
9040
|
+
y: position.y,
|
|
8591
9041
|
...pressure != null ? { pressure } : {}
|
|
8592
9042
|
};
|
|
8593
9043
|
});
|