@anaralabs/lector 3.10.2 → 3.10.4

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.
@@ -124,12 +124,6 @@ declare interface AnnotationTooltipProps {
124
124
  }) => React.ReactNode;
125
125
  }
126
126
 
127
- declare type CacheEntry = {
128
- bitmap: ImageBitmap;
129
- width: number;
130
- height: number;
131
- };
132
-
133
127
  /**
134
128
  * Calculates the highlight rectangles for a given text match.
135
129
  *
@@ -192,6 +186,8 @@ export declare const ensureAnnotationLayerStyles: () => void;
192
186
 
193
187
  export declare const firstMemo: <T>(first: boolean, memo: unknown, initializer: () => T) => T;
194
188
 
189
+ export declare const getDefaultPdfJsAssetUrls: (version: string) => PdfJsAssetUrls;
190
+
195
191
  export declare const getEndOfHighlight: (selection: ColoredHighlight) => number;
196
192
 
197
193
  export declare const getFitWidthZoom: (containerWidth: number, viewports: PageViewport[], zoomOptions: {
@@ -305,6 +301,13 @@ export declare const Pages: ({ children, gap, virtualizerOptions, initialOffset,
305
301
  onOffsetChange?: (offset: number) => void;
306
302
  }) => JSX.Element;
307
303
 
304
+ export declare interface PdfJsAssetUrls {
305
+ wasmUrl: string;
306
+ cMapUrl: string;
307
+ standardFontDataUrl: string;
308
+ iccUrl: string;
309
+ }
310
+
308
311
  export declare const PDFLinkServiceContext: Context<LinkService>;
309
312
 
310
313
  declare interface PDFState {
@@ -351,51 +354,6 @@ export declare type PDFVirtualizer = Virtualizer<any, any>;
351
354
 
352
355
  export declare const PreviousPage: () => void;
353
356
 
354
- /**
355
- * LRU cache for rendered page bitmaps. When scrolling back to a previously
356
- * rendered page at the same scale, we can draw from cache instantly instead
357
- * of re-rendering through PDF.js.
358
- *
359
- * Cache keys include a document ID to prevent collisions when multiple
360
- * <Root> instances render different PDFs on the same page.
361
- */
362
- declare class RenderCache {
363
- private cache;
364
- private maxEntries;
365
- private invalidatedDocs;
366
- constructor(maxEntries?: number);
367
- private key;
368
- get(documentId: string, pageNumber: number, scale: number, background?: string): CacheEntry | undefined;
369
- set(documentId: string, pageNumber: number, scale: number, canvas: HTMLCanvasElement, background?: string): Promise<void>;
370
- /** Remove all cached entries for a specific document (call on Root unmount). */
371
- invalidateDocument(documentId: string): void;
372
- clear(): void;
373
- }
374
-
375
- export declare const renderCache: RenderCache;
376
-
377
- /**
378
- * Serializes PDF page render operations so only one runs at a time.
379
- * This prevents multiple heavy pages from competing for CPU and blocking the UI.
380
- */
381
- declare class RenderQueue {
382
- private queue;
383
- private running;
384
- enqueue(execute: () => Promise<void>): {
385
- promise: Promise<void>;
386
- cancel: () => void;
387
- };
388
- private flush;
389
- }
390
-
391
- /**
392
- * Global singleton — intentionally shared across all <Root> instances.
393
- * Serializing renders across documents prevents overall GPU/CPU pressure
394
- * on the main thread (CanvasGraphics is single-threaded). The trade-off
395
- * is increased latency in multi-viewer setups.
396
- */
397
- export declare const renderQueue: RenderQueue;
398
-
399
357
  export declare const Root: ForwardRefExoticComponent<Omit<HTMLProps<HTMLDivElement> & usePDFDocumentParams & {
400
358
  loader?: ReactNode;
401
359
  }, "ref"> & RefAttributes<unknown>>;
package/dist/index.js CHANGED
@@ -676,40 +676,6 @@ var ensureAnnotationLayerStyles = () => {
676
676
  style.textContent = annotationLayerStyles;
677
677
  document.head.appendChild(style);
678
678
  };
679
-
680
- // src/lib/cancellable.ts
681
- var cancellable = (promise) => {
682
- let isCancelled = false;
683
- const wrappedPromise = new Promise((resolve, reject) => {
684
- promise.then(
685
- (value) => {
686
- if (!isCancelled) {
687
- resolve(value);
688
- }
689
- },
690
- (error) => {
691
- if (!isCancelled) {
692
- reject(error);
693
- }
694
- }
695
- );
696
- });
697
- return {
698
- promise: wrappedPromise,
699
- cancel() {
700
- isCancelled = true;
701
- }
702
- };
703
- };
704
-
705
- // src/lib/pdfjs.ts
706
- var pdfJsPromise = null;
707
- var loadPdfJs = () => {
708
- if (!pdfJsPromise) {
709
- pdfJsPromise = import('pdfjs-dist/legacy/build/pdf.mjs');
710
- }
711
- return pdfJsPromise;
712
- };
713
679
  var usePdfJump = () => {
714
680
  const virtualizer = usePdf((state) => state.virtualizer);
715
681
  const setHighlight = usePdf((state) => state.setHighlight);
@@ -1041,13 +1007,15 @@ var useAnnotationLayer = (params) => {
1041
1007
  if (linkService._pdfDocumentProxy !== pdfDocumentProxy) {
1042
1008
  linkService.setDocument(pdfDocumentProxy);
1043
1009
  }
1044
- annotationLayerRef.current.innerHTML = "";
1045
- annotationLayerRef.current.className = "annotationLayer";
1010
+ const container = annotationLayerRef.current;
1011
+ let cancelled = false;
1012
+ container.replaceChildren();
1013
+ container.className = "annotationLayer";
1046
1014
  const viewport = pdfPageProxy.getViewport({ scale: 1 }).clone({
1047
1015
  dontFlip: true
1048
1016
  });
1049
1017
  const annotationLayerConfig = {
1050
- div: annotationLayerRef.current,
1018
+ div: container,
1051
1019
  viewport,
1052
1020
  page: pdfPageProxy,
1053
1021
  linkService,
@@ -1058,25 +1026,26 @@ var useAnnotationLayer = (params) => {
1058
1026
  structTreeLayer: void 0,
1059
1027
  commentManager: void 0
1060
1028
  };
1061
- const { cancel } = cancellable(
1062
- (async () => {
1063
- try {
1064
- const { AnnotationLayer: AnnotationLayer2 } = await loadPdfJs();
1065
- const annotationLayer = new AnnotationLayer2(annotationLayerConfig);
1066
- annotationLayerObjectRef.current = annotationLayer;
1067
- const annotations = await pdfPageProxy.getAnnotations();
1068
- await annotationLayer.render({
1069
- ...annotationLayerConfig,
1070
- ...mergedParams,
1071
- annotations,
1072
- linkService
1073
- });
1074
- } catch (_error) {
1075
- }
1076
- })()
1077
- );
1029
+ (async () => {
1030
+ try {
1031
+ if (cancelled) return;
1032
+ const { AnnotationLayer: AnnotationLayer2 } = await import('pdfjs-dist/legacy/build/pdf.mjs');
1033
+ if (cancelled) return;
1034
+ const annotationLayer = new AnnotationLayer2(annotationLayerConfig);
1035
+ annotationLayerObjectRef.current = annotationLayer;
1036
+ const annotations = await pdfPageProxy.getAnnotations();
1037
+ if (cancelled) return;
1038
+ await annotationLayer.render({
1039
+ ...annotationLayerConfig,
1040
+ ...mergedParams,
1041
+ annotations,
1042
+ linkService
1043
+ });
1044
+ } catch (_error) {
1045
+ }
1046
+ })();
1078
1047
  return () => {
1079
- cancel();
1048
+ cancelled = true;
1080
1049
  };
1081
1050
  }, [pdfPageProxy, pdfDocumentProxy, mergedParams, linkService]);
1082
1051
  return {
@@ -1135,125 +1104,6 @@ function clampScaleForPage(targetScale, pageWidth, pageHeight) {
1135
1104
  );
1136
1105
  return Math.max(safeScale, 0);
1137
1106
  }
1138
-
1139
- // src/lib/render-cache.ts
1140
- var RenderCache = class {
1141
- cache = /* @__PURE__ */ new Map();
1142
- maxEntries;
1143
- // Tracks invalidated documents so in-flight createImageBitmap calls
1144
- // don't insert orphaned entries after invalidateDocument() runs.
1145
- invalidatedDocs = /* @__PURE__ */ new Set();
1146
- constructor(maxEntries = 60) {
1147
- this.maxEntries = maxEntries;
1148
- }
1149
- key(documentId, pageNumber, scale, background) {
1150
- return `${documentId}-${pageNumber}-${scale}-${background ?? "white"}`;
1151
- }
1152
- get(documentId, pageNumber, scale, background) {
1153
- const k = this.key(documentId, pageNumber, scale, background);
1154
- const entry = this.cache.get(k);
1155
- if (!entry) return void 0;
1156
- this.cache.delete(k);
1157
- this.cache.set(k, entry);
1158
- return entry;
1159
- }
1160
- async set(documentId, pageNumber, scale, canvas, background) {
1161
- const k = this.key(documentId, pageNumber, scale, background);
1162
- const w = canvas.width;
1163
- const h = canvas.height;
1164
- if (w === 0 || h === 0) return;
1165
- while (this.cache.size >= this.maxEntries) {
1166
- const oldest = this.cache.keys().next().value;
1167
- if (oldest !== void 0) {
1168
- const entry = this.cache.get(oldest);
1169
- entry?.bitmap.close();
1170
- this.cache.delete(oldest);
1171
- }
1172
- }
1173
- try {
1174
- const bitmap = await createImageBitmap(canvas);
1175
- if (this.invalidatedDocs.has(documentId)) {
1176
- bitmap.close();
1177
- return;
1178
- }
1179
- this.cache.set(k, {
1180
- bitmap,
1181
- width: w,
1182
- height: h
1183
- });
1184
- } catch {
1185
- }
1186
- }
1187
- /** Remove all cached entries for a specific document (call on Root unmount). */
1188
- invalidateDocument(documentId) {
1189
- this.invalidatedDocs.add(documentId);
1190
- for (const [k, entry] of this.cache) {
1191
- if (k.startsWith(`${documentId}-`)) {
1192
- entry.bitmap.close();
1193
- this.cache.delete(k);
1194
- }
1195
- }
1196
- setTimeout(() => {
1197
- this.invalidatedDocs.delete(documentId);
1198
- }, 1e3);
1199
- }
1200
- clear() {
1201
- for (const entry of this.cache.values()) {
1202
- entry.bitmap.close();
1203
- }
1204
- this.cache.clear();
1205
- this.invalidatedDocs.clear();
1206
- }
1207
- };
1208
- var renderCache = new RenderCache();
1209
-
1210
- // src/lib/render-queue.ts
1211
- var RenderQueue = class {
1212
- queue = [];
1213
- running = false;
1214
- enqueue(execute) {
1215
- let resolve;
1216
- let reject;
1217
- const promise = new Promise((res, rej) => {
1218
- resolve = res;
1219
- reject = rej;
1220
- });
1221
- const job = {
1222
- execute,
1223
- resolve,
1224
- reject,
1225
- cancelled: false
1226
- };
1227
- this.queue.push(job);
1228
- this.flush();
1229
- return {
1230
- promise,
1231
- cancel: () => {
1232
- job.cancelled = true;
1233
- job.resolve();
1234
- }
1235
- };
1236
- }
1237
- async flush() {
1238
- if (this.running) return;
1239
- this.running = true;
1240
- while (this.queue.length > 0) {
1241
- const job = this.queue.shift();
1242
- if (job.cancelled) {
1243
- job.resolve();
1244
- continue;
1245
- }
1246
- try {
1247
- await job.execute();
1248
- job.resolve();
1249
- } catch (error) {
1250
- job.reject(error);
1251
- }
1252
- }
1253
- this.running = false;
1254
- }
1255
- };
1256
- var renderQueue = new RenderQueue();
1257
1107
  var useDpr = () => {
1258
1108
  const [dpr, setDPR] = useState(
1259
1109
  typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
@@ -1277,123 +1127,149 @@ var useDpr = () => {
1277
1127
  };
1278
1128
 
1279
1129
  // src/hooks/layers/useCanvasLayer.tsx
1130
+ var CACHE_MAX = 60;
1131
+ var cacheEntries = [];
1132
+ var canvasBitmapCache = /* @__PURE__ */ new WeakMap();
1133
+ function clearBitmapCache(documentId) {
1134
+ if (documentId === void 0) {
1135
+ for (const entry of cacheEntries) {
1136
+ entry.bitmap.close();
1137
+ canvasBitmapCache.get(entry.proxy)?.delete(entry.key);
1138
+ }
1139
+ cacheEntries.length = 0;
1140
+ return;
1141
+ }
1142
+ for (let i = cacheEntries.length - 1; i >= 0; i--) {
1143
+ const entry = cacheEntries[i];
1144
+ if (entry.docId !== documentId) continue;
1145
+ entry.bitmap.close();
1146
+ canvasBitmapCache.get(entry.proxy)?.delete(entry.key);
1147
+ cacheEntries.splice(i, 1);
1148
+ }
1149
+ }
1150
+ function cacheKey(scale, background) {
1151
+ const bg = background ?? "white";
1152
+ let hash = Math.round(scale * 1e4);
1153
+ for (let i = 0; i < bg.length; i++) {
1154
+ hash = hash * 31 + bg.charCodeAt(i) | 0;
1155
+ }
1156
+ return hash;
1157
+ }
1158
+ function getCachedBitmap(proxy, scale, background) {
1159
+ const key = cacheKey(scale, background);
1160
+ const bitmap = canvasBitmapCache.get(proxy)?.get(key);
1161
+ if (!bitmap) return null;
1162
+ const idx = cacheEntries.findIndex((e) => e.proxy === proxy && e.key === key);
1163
+ if (idx !== -1 && idx !== cacheEntries.length - 1) {
1164
+ const [entry] = cacheEntries.splice(idx, 1);
1165
+ cacheEntries.push(entry);
1166
+ }
1167
+ return bitmap;
1168
+ }
1169
+ function setCachedBitmap(docId, proxy, scale, background, bitmap) {
1170
+ const key = cacheKey(scale, background);
1171
+ let map = canvasBitmapCache.get(proxy);
1172
+ if (!map) {
1173
+ map = /* @__PURE__ */ new Map();
1174
+ canvasBitmapCache.set(proxy, map);
1175
+ }
1176
+ const existing = map.get(key);
1177
+ if (existing && existing !== bitmap) {
1178
+ existing.close();
1179
+ const idx = cacheEntries.findIndex(
1180
+ (e) => e.proxy === proxy && e.key === key
1181
+ );
1182
+ if (idx !== -1) cacheEntries.splice(idx, 1);
1183
+ }
1184
+ map.set(key, bitmap);
1185
+ cacheEntries.push({ docId, proxy, key, bitmap });
1186
+ while (cacheEntries.length > CACHE_MAX) {
1187
+ const evicted = cacheEntries.shift();
1188
+ const evictedMap = canvasBitmapCache.get(evicted.proxy);
1189
+ if (evictedMap?.get(evicted.key) === evicted.bitmap) {
1190
+ evictedMap.delete(evicted.key);
1191
+ evicted.bitmap.close();
1192
+ }
1193
+ }
1194
+ }
1280
1195
  var useCanvasLayer = ({ background }) => {
1281
1196
  const canvasRef = useRef(null);
1282
1197
  const pageNumber = usePDFPageNumber();
1283
1198
  const dpr = useDpr();
1284
1199
  const bouncyZoom = usePdf((state) => state.zoom);
1200
+ const docId = usePdf((state) => state.pdfDocumentProxy.fingerprints[0] ?? "");
1285
1201
  const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
1286
1202
  const markPageRendered = usePdf((state) => state.markPageRendered);
1287
1203
  const unmarkPageRendered = usePdf((state) => state.unmarkPageRendered);
1288
- const documentId = usePdf(
1289
- (state) => state.pdfDocumentProxy.fingerprints[0] ?? "default"
1290
- );
1291
1204
  const [zoom] = useDebounce(bouncyZoom, 100);
1292
- const lastRenderedScaleRef = useRef(0);
1293
- const lastBackgroundRef = useRef(background);
1294
- const lastPageProxyRef = useRef(pdfPageProxy);
1295
- const pageDimsRef = useRef(null);
1296
- if (!pageDimsRef.current || pageDimsRef.current.proxy !== pdfPageProxy) {
1297
- const vp = pdfPageProxy.getViewport({ scale: 1 });
1298
- pageDimsRef.current = {
1299
- width: vp.width,
1300
- height: vp.height,
1301
- proxy: pdfPageProxy
1302
- };
1303
- }
1304
1205
  useLayoutEffect(() => {
1305
- if (!canvasRef.current) {
1306
- return;
1307
- }
1308
- if (lastPageProxyRef.current !== pdfPageProxy || lastBackgroundRef.current !== background) {
1309
- lastPageProxyRef.current = pdfPageProxy;
1310
- lastBackgroundRef.current = background;
1311
- lastRenderedScaleRef.current = 0;
1312
- }
1313
- const { width: pageWidth, height: pageHeight } = pageDimsRef.current;
1314
- const bgColor = background ?? "white";
1206
+ if (!canvasRef.current) return;
1207
+ const baseCanvas = canvasRef.current;
1208
+ const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
1209
+ const pageWidth = baseViewport.width;
1210
+ const pageHeight = baseViewport.height;
1315
1211
  const targetBaseScale = dpr * Math.min(zoom, 1);
1316
1212
  const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
1317
- if (baseScale === lastRenderedScaleRef.current) {
1318
- return;
1319
- }
1320
- const w = Math.floor(pageWidth * baseScale);
1321
- const h = Math.floor(pageHeight * baseScale);
1322
- const cssText = `position:absolute;top:0;left:0;width:${pageWidth}px;height:${pageHeight}px;transform:translate(0px,0px);z-index:0;pointer-events:none;background-color:${bgColor}`;
1323
- const swapToCanvas = (source, sw, sh) => {
1324
- const canvas = canvasRef.current;
1325
- if (!canvas) return;
1326
- canvas.width = sw;
1327
- canvas.height = sh;
1328
- canvas.style.cssText = cssText;
1329
- const ctx = canvas.getContext("2d");
1330
- if (ctx) {
1331
- ctx.drawImage(source, 0, 0);
1332
- }
1333
- lastRenderedScaleRef.current = baseScale;
1334
- };
1335
- const cached = renderCache.get(
1336
- documentId,
1337
- pageNumber,
1338
- baseScale,
1339
- background
1340
- );
1213
+ baseCanvas.width = Math.floor(pageWidth * baseScale);
1214
+ baseCanvas.height = Math.floor(pageHeight * baseScale);
1215
+ baseCanvas.style.position = "absolute";
1216
+ baseCanvas.style.top = "0";
1217
+ baseCanvas.style.left = "0";
1218
+ baseCanvas.style.width = `${pageWidth}px`;
1219
+ baseCanvas.style.height = `${pageHeight}px`;
1220
+ baseCanvas.style.transform = "translate(0px, 0px)";
1221
+ baseCanvas.style.zIndex = "0";
1222
+ baseCanvas.style.pointerEvents = "none";
1223
+ baseCanvas.style.backgroundColor = background ?? "white";
1224
+ baseCanvas.style.visibility = "";
1225
+ const context = baseCanvas.getContext("2d");
1226
+ if (!context) return;
1227
+ const cached = getCachedBitmap(pdfPageProxy, baseScale, background);
1341
1228
  if (cached) {
1342
- swapToCanvas(cached.bitmap, cached.width, cached.height);
1229
+ context.drawImage(cached, 0, 0);
1343
1230
  markPageRendered(pageNumber);
1344
1231
  return;
1345
1232
  }
1233
+ context.setTransform(1, 0, 0, 1, 0, 0);
1234
+ context.clearRect(0, 0, baseCanvas.width, baseCanvas.height);
1235
+ baseCanvas.style.visibility = "hidden";
1346
1236
  let cancelled = false;
1347
- let activeRenderingTask = null;
1348
- const job = renderQueue.enqueue(() => {
1349
- return new Promise((resolve, reject) => {
1350
- if (cancelled || !canvasRef.current) {
1351
- resolve();
1352
- return;
1353
- }
1354
- const buffer = document.createElement("canvas");
1355
- buffer.width = w;
1356
- buffer.height = h;
1357
- const bufferCtx = buffer.getContext("2d");
1358
- if (!bufferCtx) {
1359
- resolve();
1360
- return;
1361
- }
1362
- const viewport = pdfPageProxy.getViewport({ scale: baseScale });
1363
- const renderingTask = pdfPageProxy.render({
1364
- canvas: buffer,
1365
- canvasContext: bufferCtx,
1366
- viewport,
1367
- background
1368
- });
1369
- activeRenderingTask = renderingTask;
1370
- renderingTask.promise.then(() => {
1371
- activeRenderingTask = null;
1372
- if (cancelled || !canvasRef.current) {
1373
- resolve();
1374
- return;
1375
- }
1376
- swapToCanvas(buffer, w, h);
1377
- markPageRendered(pageNumber);
1378
- renderCache.set(documentId, pageNumber, baseScale, buffer, background).finally(() => {
1379
- buffer.width = 0;
1380
- buffer.height = 0;
1381
- });
1382
- resolve();
1383
- }).catch((error) => {
1384
- activeRenderingTask = null;
1385
- if (error.name === "RenderingCancelledException") {
1386
- resolve();
1237
+ const viewport = pdfPageProxy.getViewport({ scale: baseScale });
1238
+ const renderingTask = pdfPageProxy.render({
1239
+ canvas: baseCanvas,
1240
+ canvasContext: context,
1241
+ viewport,
1242
+ background
1243
+ });
1244
+ renderingTask.promise.then(() => {
1245
+ if (cancelled) return;
1246
+ baseCanvas.style.visibility = "";
1247
+ markPageRendered(pageNumber);
1248
+ if (typeof createImageBitmap !== "undefined") {
1249
+ createImageBitmap(baseCanvas).then((bitmap) => {
1250
+ if (cancelled) {
1251
+ bitmap.close();
1387
1252
  return;
1388
1253
  }
1389
- reject(error);
1254
+ setCachedBitmap(
1255
+ docId,
1256
+ pdfPageProxy,
1257
+ baseScale,
1258
+ background,
1259
+ bitmap
1260
+ );
1261
+ }).catch(() => {
1390
1262
  });
1391
- });
1263
+ }
1264
+ }).catch((error) => {
1265
+ if (cancelled) return;
1266
+ if (error?.name === "RenderingCancelledException") return;
1267
+ baseCanvas.style.visibility = "";
1268
+ console.error("PDF render error:", error);
1392
1269
  });
1393
1270
  return () => {
1394
1271
  cancelled = true;
1395
- job.cancel();
1396
- void activeRenderingTask?.cancel();
1272
+ void renderingTask.cancel();
1397
1273
  };
1398
1274
  }, [
1399
1275
  pdfPageProxy,
@@ -1401,16 +1277,16 @@ var useCanvasLayer = ({ background }) => {
1401
1277
  dpr,
1402
1278
  zoom,
1403
1279
  pageNumber,
1404
- documentId,
1405
- markPageRendered
1280
+ markPageRendered,
1281
+ docId
1406
1282
  ]);
1407
1283
  useEffect(() => {
1408
1284
  const canvas = canvasRef.current;
1409
1285
  return () => {
1410
1286
  unmarkPageRendered(pageNumber);
1411
1287
  if (canvas) {
1412
- canvas.width = 1;
1413
- canvas.height = 1;
1288
+ canvas.width = 0;
1289
+ canvas.height = 0;
1414
1290
  }
1415
1291
  };
1416
1292
  }, [pageNumber, unmarkPageRendered]);
@@ -2647,10 +2523,8 @@ var useTextLayer = () => {
2647
2523
  textLayerRef.current.cancel();
2648
2524
  textLayerRef.current = null;
2649
2525
  }
2650
- void loadPdfJs().then(({ TextLayer: TextLayer2 }) => {
2651
- if (isCancelled || textContainerRef.current !== textContainer) {
2652
- return;
2653
- }
2526
+ void import('pdfjs-dist/legacy/build/pdf.mjs').then(({ TextLayer: TextLayer2 }) => {
2527
+ if (isCancelled || textContainerRef.current !== textContainer) return;
2654
2528
  const textLayer = new TextLayer2({
2655
2529
  textContentSource: pdfPageProxy.streamTextContent(),
2656
2530
  container: textContainer,
@@ -2659,8 +2533,7 @@ var useTextLayer = () => {
2659
2533
  textLayerRef.current = textLayer;
2660
2534
  return textLayer.render();
2661
2535
  }).then(() => {
2662
- const textLayer = textLayerRef.current;
2663
- if (!textLayer || isCancelled || textContainerRef.current !== textContainer) {
2536
+ if (isCancelled || textContainerRef.current !== textContainer) {
2664
2537
  return;
2665
2538
  }
2666
2539
  const endOfContent = document.createElement("div");
@@ -2668,7 +2541,7 @@ var useTextLayer = () => {
2668
2541
  textContainer.appendChild(endOfContent);
2669
2542
  bindMouseEvents(textContainer, endOfContent);
2670
2543
  }).catch((error) => {
2671
- if (error.name !== "AbortException") {
2544
+ if (error instanceof Error && error.name !== "AbortException") {
2672
2545
  console.error("TextLayer rendering error:", error);
2673
2546
  }
2674
2547
  }).finally(() => {
@@ -2715,6 +2588,33 @@ var TextLayer = memo(
2715
2588
  }
2716
2589
  );
2717
2590
  TextLayer.displayName = "TextLayer";
2591
+
2592
+ // src/lib/cancellable.ts
2593
+ var cancellable = (promise) => {
2594
+ let isCancelled = false;
2595
+ const wrappedPromise = new Promise((resolve, reject) => {
2596
+ promise.then(
2597
+ (value) => {
2598
+ if (!isCancelled) {
2599
+ resolve(value);
2600
+ }
2601
+ },
2602
+ (error) => {
2603
+ if (!isCancelled) {
2604
+ reject(error);
2605
+ }
2606
+ }
2607
+ );
2608
+ });
2609
+ return {
2610
+ promise: wrappedPromise,
2611
+ cancel() {
2612
+ isCancelled = true;
2613
+ }
2614
+ };
2615
+ };
2616
+
2617
+ // src/hooks/usePdfOutline.tsx
2718
2618
  var usePDFOutline = () => {
2719
2619
  const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
2720
2620
  const [outline, setOutline] = useState();
@@ -5337,7 +5237,28 @@ var Pages = ({
5337
5237
  }
5338
5238
  );
5339
5239
  };
5340
- function buildDocumentInitParams(source, overrides) {
5240
+
5241
+ // src/lib/pdfjs.ts
5242
+ var pdfJsPromise = null;
5243
+ var loadPdfJs = () => {
5244
+ if (!pdfJsPromise) {
5245
+ pdfJsPromise = import('pdfjs-dist/legacy/build/pdf.mjs');
5246
+ }
5247
+ return pdfJsPromise;
5248
+ };
5249
+ var ASSET_CDN_BASE = "https://cdn.jsdelivr.net/npm/pdfjs-dist";
5250
+ var getDefaultPdfJsAssetUrls = (version) => {
5251
+ const base = `${ASSET_CDN_BASE}@${version}`;
5252
+ return {
5253
+ wasmUrl: `${base}/wasm/`,
5254
+ cMapUrl: `${base}/cmaps/`,
5255
+ standardFontDataUrl: `${base}/standard_fonts/`,
5256
+ iccUrl: `${base}/iccs/`
5257
+ };
5258
+ };
5259
+
5260
+ // src/hooks/document/document.ts
5261
+ function buildDocumentInitParams(source, pdfJsVersion, overrides) {
5341
5262
  let params;
5342
5263
  if (typeof source === "string" || source instanceof URL) {
5343
5264
  params = { url: source };
@@ -5346,6 +5267,7 @@ function buildDocumentInitParams(source, overrides) {
5346
5267
  } else {
5347
5268
  params = { ...source };
5348
5269
  }
5270
+ const assetUrls = getDefaultPdfJsAssetUrls(pdfJsVersion);
5349
5271
  const defaults = {
5350
5272
  // Skip the slow _guessMax binary-search auto-detection of max canvas area.
5351
5273
  // 1 GiB = 256 megapixels at 4 bytes/pixel. If the device can't allocate
@@ -5357,7 +5279,22 @@ function buildDocumentInitParams(source, overrides) {
5357
5279
  enableHWA: true,
5358
5280
  // Explicitly opt-in to OffscreenCanvas in the worker for image processing.
5359
5281
  // Already the browser default, but being explicit avoids edge cases.
5360
- isOffscreenCanvasSupported: true
5282
+ isOffscreenCanvasSupported: true,
5283
+ // PDF.js 5.x split out auxiliary resources that are loaded on demand.
5284
+ // Without these URLs, several content types render as blank pages
5285
+ // because pdf.js silently fails to load decoders / fonts / colour
5286
+ // profiles. The most visible symptom: scanned PDFs with JPEG2000 or
5287
+ // JBIG2 images (Internet Archive, government archives) render as
5288
+ // completely blank pages — every image fails with
5289
+ // "OpenJPEG failed to initialize". Defaulting these to a versioned
5290
+ // jsDelivr URL gets the viewer working out of the box; production
5291
+ // deployments should self-host and pass overrides via
5292
+ // `documentOptions`.
5293
+ wasmUrl: assetUrls.wasmUrl,
5294
+ cMapUrl: assetUrls.cMapUrl,
5295
+ cMapPacked: true,
5296
+ standardFontDataUrl: assetUrls.standardFontDataUrl,
5297
+ iccUrl: assetUrls.iccUrl
5361
5298
  };
5362
5299
  return { ...defaults, ...params, ...overrides };
5363
5300
  }
@@ -5410,12 +5347,16 @@ var usePDFDocumentContext = ({
5410
5347
  setProgress(0);
5411
5348
  let loadingTask = null;
5412
5349
  let isDisposed = false;
5413
- void loadPdfJs().then(({ getDocument }) => {
5350
+ void loadPdfJs().then(({ getDocument, version }) => {
5414
5351
  if (isDisposed) {
5415
5352
  return;
5416
5353
  }
5417
5354
  loadingTask = getDocument(
5418
- buildDocumentInitParams(source, documentOptionsRef.current)
5355
+ buildDocumentInitParams(
5356
+ source,
5357
+ version,
5358
+ documentOptionsRef.current
5359
+ )
5419
5360
  );
5420
5361
  loadingTask.onProgress = (progressEvent) => {
5421
5362
  if (progressEvent.loaded === progressEvent.total) {
@@ -5476,11 +5417,11 @@ var Root = forwardRef(
5476
5417
  const linkService = useCreatePDFLinkService(
5477
5418
  initialState?.pdfDocumentProxy ?? null
5478
5419
  );
5479
- const documentId = initialState?.pdfDocumentProxy.fingerprints[0];
5420
+ const documentId = initialState?.pdfDocumentProxy?.fingerprints?.[0];
5480
5421
  useEffect(() => {
5481
5422
  if (!documentId) return;
5482
5423
  return () => {
5483
- renderCache.invalidateDocument(documentId);
5424
+ clearBitmapCache(documentId);
5484
5425
  };
5485
5426
  }, [documentId]);
5486
5427
  return /* @__PURE__ */ jsx(Primitive.div, { ref, ...props, children: initialState ? /* @__PURE__ */ jsx(PDFStore.Provider, { initialValue: initialState, children: /* @__PURE__ */ jsx(PDFLinkServiceContext.Provider, { value: linkService, children }) }) : loader ?? "Loading..." });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anaralabs/lector",
3
- "version": "3.10.2",
3
+ "version": "3.10.4",
4
4
  "description": "A headless PDF viewer for React",
5
5
  "author": "andrewdr (https://github.com/andrewdoro)",
6
6
  "license": "MIT",