@anaralabs/lector 3.10.0 → 3.10.1
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/_tsup-dts-rollup.d.ts +72 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +399 -157
- package/package.json +2 -2
|
@@ -38,7 +38,7 @@ declare interface Annotation {
|
|
|
38
38
|
export { Annotation }
|
|
39
39
|
export { Annotation as Annotation_alias_1 }
|
|
40
40
|
|
|
41
|
-
export declare const AnnotationHighlightLayer: ({ className, style, renderTooltipContent, renderHoverTooltipContent, tooltipClassName, highlightClassName, underlineClassName, commentIconPosition, commmentIcon, commentIconClassName, focusedAnnotationId, focusedHoverAnnotationId, onAnnotationClick, onAnnotationTooltipClose, hoverTooltipClassName, }: AnnotationHighlightLayerProps) => JSX.Element;
|
|
41
|
+
export declare const AnnotationHighlightLayer: ({ className, style, renderTooltipContent, renderHoverTooltipContent, tooltipClassName, highlightClassName, underlineClassName, commentIconPosition, commmentIcon, commentIconClassName, focusedAnnotationId, focusedHoverAnnotationId, onAnnotationClick, onAnnotationTooltipClose, hoverTooltipClassName, }: AnnotationHighlightLayerProps) => JSX.Element | null;
|
|
42
42
|
|
|
43
43
|
declare interface AnnotationHighlightLayerProps {
|
|
44
44
|
className?: string;
|
|
@@ -124,6 +124,12 @@ 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
|
+
|
|
127
133
|
/**
|
|
128
134
|
* Calculates the highlight rectangles for a given text match.
|
|
129
135
|
*
|
|
@@ -149,6 +155,8 @@ background?: string;
|
|
|
149
155
|
|
|
150
156
|
export declare const clamp: (value: number, minimum: number, maximum: number) => number;
|
|
151
157
|
|
|
158
|
+
export declare function clampScaleForPage(targetScale: number, pageWidth: number, pageHeight: number): number;
|
|
159
|
+
|
|
152
160
|
declare type CollapsibleSelection = {
|
|
153
161
|
highlights: HighlightRect[];
|
|
154
162
|
underlines?: HighlightRect[];
|
|
@@ -257,6 +265,10 @@ export declare class LinkService {
|
|
|
257
265
|
|
|
258
266
|
export declare const loadPdfJs: () => Promise<pdfjsDist>;
|
|
259
267
|
|
|
268
|
+
export declare const MAX_CANVAS_DIMENSION = 32767;
|
|
269
|
+
|
|
270
|
+
export declare const MAX_CANVAS_PIXELS = 16777216;
|
|
271
|
+
|
|
260
272
|
export declare const NextPage: () => void;
|
|
261
273
|
|
|
262
274
|
export declare const Outline: ({ children, ...props }: HTMLProps<HTMLUListElement> & {
|
|
@@ -321,6 +333,9 @@ declare interface PDFState {
|
|
|
321
333
|
coloredHighlights: ColoredHighlight[];
|
|
322
334
|
addColoredHighlight: (value: ColoredHighlight) => void;
|
|
323
335
|
deleteColoredHighlight: (uuid: string) => void;
|
|
336
|
+
renderedPages: Record<number, true>;
|
|
337
|
+
markPageRendered: (pageNumber: number) => void;
|
|
338
|
+
unmarkPageRendered: (pageNumber: number) => void;
|
|
324
339
|
}
|
|
325
340
|
|
|
326
341
|
export declare const PDFStore: {
|
|
@@ -336,6 +351,51 @@ export declare type PDFVirtualizer = Virtualizer<any, any>;
|
|
|
336
351
|
|
|
337
352
|
export declare const PreviousPage: () => void;
|
|
338
353
|
|
|
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
|
+
|
|
339
399
|
export declare const Root: ForwardRefExoticComponent<Omit<HTMLProps<HTMLDivElement> & usePDFDocumentParams & {
|
|
340
400
|
loader?: ReactNode;
|
|
341
401
|
}, "ref"> & RefAttributes<unknown>>;
|
|
@@ -426,11 +486,15 @@ declare interface UseAnnotationTooltipReturn {
|
|
|
426
486
|
|
|
427
487
|
export declare const useCreatePDFLinkService: (pdfDocumentProxy: PDFDocumentProxy | null) => LinkService;
|
|
428
488
|
|
|
489
|
+
declare const usePageRendered: (pageNumber: number) => boolean;
|
|
490
|
+
export { usePageRendered }
|
|
491
|
+
export { usePageRendered as usePageRendered_alias_1 }
|
|
492
|
+
|
|
429
493
|
declare const usePdf: <T>(selector: (state: PDFState) => T) => T;
|
|
430
494
|
export { usePdf }
|
|
431
495
|
export { usePdf as usePdf_alias_1 }
|
|
432
496
|
|
|
433
|
-
export declare const usePDFDocumentContext: ({ onDocumentLoad, source, initialRotation, isZoomFitWidth, zoom, zoomOptions, }: usePDFDocumentParams) => {
|
|
497
|
+
export declare const usePDFDocumentContext: ({ onDocumentLoad, source, initialRotation, isZoomFitWidth, zoom, zoomOptions, documentOptions, }: usePDFDocumentParams) => {
|
|
434
498
|
initialState: InitialPDFState | null | undefined;
|
|
435
499
|
};
|
|
436
500
|
|
|
@@ -447,6 +511,12 @@ export declare interface usePDFDocumentParams {
|
|
|
447
511
|
isZoomFitWidth?: boolean;
|
|
448
512
|
zoom?: number;
|
|
449
513
|
zoomOptions?: ZoomOptions;
|
|
514
|
+
/**
|
|
515
|
+
* Override or extend the PDF.js DocumentInitParameters passed to getDocument().
|
|
516
|
+
* These take highest precedence over both the source object and lector's defaults.
|
|
517
|
+
* Must be a stable reference (module-level constant or useMemo) to avoid reloading the document.
|
|
518
|
+
*/
|
|
519
|
+
documentOptions?: Partial<DocumentInitParameters>;
|
|
450
520
|
}
|
|
451
521
|
|
|
452
522
|
export declare const usePdfJump: () => {
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ export { useSearch } from './_tsup-dts-rollup.js';
|
|
|
31
31
|
export { calculateHighlightRects } from './_tsup-dts-rollup.js';
|
|
32
32
|
export { Annotation } from './_tsup-dts-rollup.js';
|
|
33
33
|
export { useAnnotations } from './_tsup-dts-rollup.js';
|
|
34
|
+
export { usePageRendered } from './_tsup-dts-rollup.js';
|
|
34
35
|
export { LinkService } from './_tsup-dts-rollup.js';
|
|
35
36
|
export { PDFLinkServiceContext } from './_tsup-dts-rollup.js';
|
|
36
37
|
export { useCreatePDFLinkService } from './_tsup-dts-rollup.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext, memo, forwardRef, cloneElement, useRef, useState, useCallback, useContext,
|
|
3
|
+
import React, { createContext, memo, forwardRef, useMemo, cloneElement, useEffect, useRef, useState, useCallback, useContext, useLayoutEffect, createRef } from 'react';
|
|
4
4
|
import { createPortal } from 'react-dom';
|
|
5
5
|
import { useFloating, autoUpdate, offset, shift, useDismiss, useInteractions, inline, flip } from '@floating-ui/react';
|
|
6
6
|
import { create, useStore, createStore } from 'zustand';
|
|
@@ -124,7 +124,19 @@ var PDFStore = createZustandContext(
|
|
|
124
124
|
coloredHighlights: prevState.coloredHighlights.filter(
|
|
125
125
|
(rect) => rect.uuid !== uuid
|
|
126
126
|
)
|
|
127
|
-
}))
|
|
127
|
+
})),
|
|
128
|
+
renderedPages: {},
|
|
129
|
+
markPageRendered: (pageNumber) => {
|
|
130
|
+
const current = get().renderedPages;
|
|
131
|
+
if (current[pageNumber]) return;
|
|
132
|
+
set({ renderedPages: { ...current, [pageNumber]: true } });
|
|
133
|
+
},
|
|
134
|
+
unmarkPageRendered: (pageNumber) => {
|
|
135
|
+
const current = get().renderedPages;
|
|
136
|
+
if (!current[pageNumber]) return;
|
|
137
|
+
const { [pageNumber]: _, ...rest } = current;
|
|
138
|
+
set({ renderedPages: rest });
|
|
139
|
+
}
|
|
128
140
|
}));
|
|
129
141
|
}
|
|
130
142
|
);
|
|
@@ -462,12 +474,15 @@ var AnnotationHighlightLayer = ({
|
|
|
462
474
|
}) => {
|
|
463
475
|
const { annotations } = useAnnotations();
|
|
464
476
|
const pageNumber = usePDFPageNumber();
|
|
465
|
-
const
|
|
466
|
-
|
|
477
|
+
const isPageRendered = usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
478
|
+
const pageAnnotations = useMemo(
|
|
479
|
+
() => annotations.filter((a) => a.pageNumber === pageNumber),
|
|
480
|
+
[annotations, pageNumber]
|
|
467
481
|
);
|
|
482
|
+
if (!isPageRendered) return null;
|
|
468
483
|
const getCommentIconPosition = (highlights) => {
|
|
469
484
|
if (!highlights.length) return { top: 0, right: 10 };
|
|
470
|
-
const sortedHighlights =
|
|
485
|
+
const sortedHighlights = highlights.toSorted((a, b) => {
|
|
471
486
|
const topDiff = a.top - b.top;
|
|
472
487
|
return Math.abs(topDiff) < 3 ? a.left - b.left : topDiff;
|
|
473
488
|
});
|
|
@@ -691,7 +706,7 @@ var cancellable = (promise) => {
|
|
|
691
706
|
var pdfJsPromise = null;
|
|
692
707
|
var loadPdfJs = () => {
|
|
693
708
|
if (!pdfJsPromise) {
|
|
694
|
-
pdfJsPromise = import('pdfjs-dist');
|
|
709
|
+
pdfJsPromise = import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
695
710
|
}
|
|
696
711
|
return pdfJsPromise;
|
|
697
712
|
};
|
|
@@ -1099,6 +1114,146 @@ var AnnotationLayer = memo(
|
|
|
1099
1114
|
}
|
|
1100
1115
|
);
|
|
1101
1116
|
AnnotationLayer.displayName = "AnnotationLayer";
|
|
1117
|
+
|
|
1118
|
+
// src/lib/canvas-utils.ts
|
|
1119
|
+
var MAX_CANVAS_PIXELS = 16777216;
|
|
1120
|
+
var MAX_CANVAS_DIMENSION = 32767;
|
|
1121
|
+
function clampScaleForPage(targetScale, pageWidth, pageHeight) {
|
|
1122
|
+
if (!targetScale) {
|
|
1123
|
+
return 0;
|
|
1124
|
+
}
|
|
1125
|
+
const areaLimit = Math.sqrt(
|
|
1126
|
+
MAX_CANVAS_PIXELS / Math.max(pageWidth * pageHeight, 1)
|
|
1127
|
+
);
|
|
1128
|
+
const widthLimit = MAX_CANVAS_DIMENSION / Math.max(pageWidth, 1);
|
|
1129
|
+
const heightLimit = MAX_CANVAS_DIMENSION / Math.max(pageHeight, 1);
|
|
1130
|
+
const safeScale = Math.min(
|
|
1131
|
+
targetScale,
|
|
1132
|
+
Number.isFinite(areaLimit) ? areaLimit : targetScale,
|
|
1133
|
+
Number.isFinite(widthLimit) ? widthLimit : targetScale,
|
|
1134
|
+
Number.isFinite(heightLimit) ? heightLimit : targetScale
|
|
1135
|
+
);
|
|
1136
|
+
return Math.max(safeScale, 0);
|
|
1137
|
+
}
|
|
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 = 30) {
|
|
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();
|
|
1102
1257
|
var useDpr = () => {
|
|
1103
1258
|
const [dpr, setDPR] = useState(
|
|
1104
1259
|
typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
|
|
@@ -1122,79 +1277,136 @@ var useDpr = () => {
|
|
|
1122
1277
|
};
|
|
1123
1278
|
|
|
1124
1279
|
// src/hooks/layers/useCanvasLayer.tsx
|
|
1125
|
-
var MAX_CANVAS_PIXELS = 16777216;
|
|
1126
|
-
var MAX_CANVAS_DIMENSION = 32767;
|
|
1127
1280
|
var useCanvasLayer = ({ background }) => {
|
|
1128
1281
|
const canvasRef = useRef(null);
|
|
1129
1282
|
const pageNumber = usePDFPageNumber();
|
|
1130
1283
|
const dpr = useDpr();
|
|
1131
1284
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1132
1285
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1133
|
-
const
|
|
1134
|
-
const
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
return 0;
|
|
1138
|
-
}
|
|
1139
|
-
const areaLimit = Math.sqrt(
|
|
1140
|
-
MAX_CANVAS_PIXELS / Math.max(pageWidth * pageHeight, 1)
|
|
1141
|
-
);
|
|
1142
|
-
const widthLimit = MAX_CANVAS_DIMENSION / Math.max(pageWidth, 1);
|
|
1143
|
-
const heightLimit = MAX_CANVAS_DIMENSION / Math.max(pageHeight, 1);
|
|
1144
|
-
const safeScale = Math.min(
|
|
1145
|
-
targetScale,
|
|
1146
|
-
Number.isFinite(areaLimit) ? areaLimit : targetScale,
|
|
1147
|
-
Number.isFinite(widthLimit) ? widthLimit : targetScale,
|
|
1148
|
-
Number.isFinite(heightLimit) ? heightLimit : targetScale
|
|
1149
|
-
);
|
|
1150
|
-
return Math.max(safeScale, 0);
|
|
1151
|
-
},
|
|
1152
|
-
[]
|
|
1286
|
+
const markPageRendered = usePdf((state) => state.markPageRendered);
|
|
1287
|
+
const unmarkPageRendered = usePdf((state) => state.unmarkPageRendered);
|
|
1288
|
+
const documentId = usePdf(
|
|
1289
|
+
(state) => state.pdfDocumentProxy.fingerprints[0] ?? "default"
|
|
1153
1290
|
);
|
|
1291
|
+
const [zoom] = useDebounce(bouncyZoom, 100);
|
|
1292
|
+
const lastRenderedScaleRef = useRef(0);
|
|
1293
|
+
const lastBackgroundRef = useRef(background);
|
|
1294
|
+
const lastPageProxyRef = useRef(pdfPageProxy);
|
|
1154
1295
|
useLayoutEffect(() => {
|
|
1155
1296
|
if (!canvasRef.current) {
|
|
1156
1297
|
return;
|
|
1157
1298
|
}
|
|
1158
|
-
|
|
1299
|
+
if (lastPageProxyRef.current !== pdfPageProxy || lastBackgroundRef.current !== background) {
|
|
1300
|
+
lastPageProxyRef.current = pdfPageProxy;
|
|
1301
|
+
lastBackgroundRef.current = background;
|
|
1302
|
+
lastRenderedScaleRef.current = 0;
|
|
1303
|
+
}
|
|
1159
1304
|
const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
1160
1305
|
const pageWidth = baseViewport.width;
|
|
1161
1306
|
const pageHeight = baseViewport.height;
|
|
1307
|
+
const bgColor = background ?? "white";
|
|
1162
1308
|
const targetBaseScale = dpr * Math.min(zoom, 1);
|
|
1163
1309
|
const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
|
|
1164
|
-
|
|
1165
|
-
baseCanvas.height = Math.floor(pageHeight * baseScale);
|
|
1166
|
-
baseCanvas.style.position = "absolute";
|
|
1167
|
-
baseCanvas.style.top = "0";
|
|
1168
|
-
baseCanvas.style.left = "0";
|
|
1169
|
-
baseCanvas.style.width = `${pageWidth}px`;
|
|
1170
|
-
baseCanvas.style.height = `${pageHeight}px`;
|
|
1171
|
-
baseCanvas.style.transform = "translate(0px, 0px)";
|
|
1172
|
-
baseCanvas.style.zIndex = "0";
|
|
1173
|
-
baseCanvas.style.pointerEvents = "none";
|
|
1174
|
-
baseCanvas.style.backgroundColor = background ?? "white";
|
|
1175
|
-
const context = baseCanvas.getContext("2d");
|
|
1176
|
-
if (!context) {
|
|
1310
|
+
if (baseScale === lastRenderedScaleRef.current) {
|
|
1177
1311
|
return;
|
|
1178
1312
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const
|
|
1182
|
-
const
|
|
1183
|
-
canvas
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
1313
|
+
const w = Math.floor(pageWidth * baseScale);
|
|
1314
|
+
const h = Math.floor(pageHeight * baseScale);
|
|
1315
|
+
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}`;
|
|
1316
|
+
const swapToCanvas = (source, sw, sh) => {
|
|
1317
|
+
const canvas = canvasRef.current;
|
|
1318
|
+
if (!canvas) return;
|
|
1319
|
+
canvas.width = sw;
|
|
1320
|
+
canvas.height = sh;
|
|
1321
|
+
canvas.style.cssText = cssText;
|
|
1322
|
+
const ctx = canvas.getContext("2d");
|
|
1323
|
+
if (ctx) {
|
|
1324
|
+
ctx.drawImage(source, 0, 0);
|
|
1191
1325
|
}
|
|
1192
|
-
|
|
1326
|
+
lastRenderedScaleRef.current = baseScale;
|
|
1327
|
+
};
|
|
1328
|
+
const cached = renderCache.get(
|
|
1329
|
+
documentId,
|
|
1330
|
+
pageNumber,
|
|
1331
|
+
baseScale,
|
|
1332
|
+
background
|
|
1333
|
+
);
|
|
1334
|
+
if (cached) {
|
|
1335
|
+
swapToCanvas(cached.bitmap, cached.width, cached.height);
|
|
1336
|
+
markPageRendered(pageNumber);
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
let cancelled = false;
|
|
1340
|
+
let activeRenderingTask = null;
|
|
1341
|
+
const job = renderQueue.enqueue(() => {
|
|
1342
|
+
return new Promise((resolve, reject) => {
|
|
1343
|
+
if (cancelled || !canvasRef.current) {
|
|
1344
|
+
resolve();
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
const buffer = document.createElement("canvas");
|
|
1348
|
+
buffer.width = w;
|
|
1349
|
+
buffer.height = h;
|
|
1350
|
+
const bufferCtx = buffer.getContext("2d");
|
|
1351
|
+
if (!bufferCtx) {
|
|
1352
|
+
resolve();
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
const viewport = pdfPageProxy.getViewport({ scale: baseScale });
|
|
1356
|
+
const renderingTask = pdfPageProxy.render({
|
|
1357
|
+
canvas: buffer,
|
|
1358
|
+
canvasContext: bufferCtx,
|
|
1359
|
+
viewport,
|
|
1360
|
+
background
|
|
1361
|
+
});
|
|
1362
|
+
activeRenderingTask = renderingTask;
|
|
1363
|
+
renderingTask.promise.then(() => {
|
|
1364
|
+
activeRenderingTask = null;
|
|
1365
|
+
if (cancelled || !canvasRef.current) {
|
|
1366
|
+
resolve();
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
swapToCanvas(buffer, w, h);
|
|
1370
|
+
markPageRendered(pageNumber);
|
|
1371
|
+
renderCache.set(documentId, pageNumber, baseScale, buffer, background).finally(() => {
|
|
1372
|
+
buffer.width = 0;
|
|
1373
|
+
buffer.height = 0;
|
|
1374
|
+
});
|
|
1375
|
+
resolve();
|
|
1376
|
+
}).catch((error) => {
|
|
1377
|
+
activeRenderingTask = null;
|
|
1378
|
+
if (error.name === "RenderingCancelledException") {
|
|
1379
|
+
resolve();
|
|
1380
|
+
return;
|
|
1381
|
+
}
|
|
1382
|
+
reject(error);
|
|
1383
|
+
});
|
|
1384
|
+
});
|
|
1193
1385
|
});
|
|
1194
1386
|
return () => {
|
|
1195
|
-
|
|
1387
|
+
cancelled = true;
|
|
1388
|
+
job.cancel();
|
|
1389
|
+
void activeRenderingTask?.cancel();
|
|
1196
1390
|
};
|
|
1197
|
-
}, [
|
|
1391
|
+
}, [
|
|
1392
|
+
pdfPageProxy,
|
|
1393
|
+
background,
|
|
1394
|
+
dpr,
|
|
1395
|
+
zoom,
|
|
1396
|
+
pageNumber,
|
|
1397
|
+
documentId,
|
|
1398
|
+
markPageRendered
|
|
1399
|
+
]);
|
|
1400
|
+
useEffect(() => {
|
|
1401
|
+
const canvas = canvasRef.current;
|
|
1402
|
+
return () => {
|
|
1403
|
+
unmarkPageRendered(pageNumber);
|
|
1404
|
+
if (canvas) {
|
|
1405
|
+
canvas.width = 1;
|
|
1406
|
+
canvas.height = 1;
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1409
|
+
}, [pageNumber, unmarkPageRendered]);
|
|
1198
1410
|
return {
|
|
1199
1411
|
canvasRef
|
|
1200
1412
|
};
|
|
@@ -1264,8 +1476,6 @@ var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
|
1264
1476
|
};
|
|
1265
1477
|
|
|
1266
1478
|
// src/hooks/layers/useDetailCanvasLayer.tsx
|
|
1267
|
-
var MAX_CANVAS_PIXELS2 = 16777216;
|
|
1268
|
-
var MAX_CANVAS_DIMENSION2 = 32767;
|
|
1269
1479
|
var DETAIL_RENDER_IDLE_MS = 80;
|
|
1270
1480
|
var useDetailCanvasLayer = ({
|
|
1271
1481
|
background,
|
|
@@ -1280,57 +1490,24 @@ var useDetailCanvasLayer = ({
|
|
|
1280
1490
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1281
1491
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1282
1492
|
const [zoom] = useDebounce(bouncyZoom, 200);
|
|
1283
|
-
const getDetailCanvas = useCallback(() => {
|
|
1284
|
-
const detailCanvas = detailCanvasRef.current;
|
|
1285
|
-
if (!detailCanvas) {
|
|
1286
|
-
return null;
|
|
1287
|
-
}
|
|
1288
|
-
detailCanvas.style.position = "absolute";
|
|
1289
|
-
detailCanvas.style.top = "0";
|
|
1290
|
-
detailCanvas.style.left = "0";
|
|
1291
|
-
detailCanvas.style.pointerEvents = "none";
|
|
1292
|
-
detailCanvas.style.zIndex = "1";
|
|
1293
|
-
detailCanvas.style.backgroundColor = background ?? "white";
|
|
1294
|
-
return detailCanvas;
|
|
1295
|
-
}, [background]);
|
|
1296
|
-
const clampScaleForPage = useCallback(
|
|
1297
|
-
(targetScale, pageWidth, pageHeight) => {
|
|
1298
|
-
if (!targetScale) {
|
|
1299
|
-
return 0;
|
|
1300
|
-
}
|
|
1301
|
-
const areaLimit = Math.sqrt(
|
|
1302
|
-
MAX_CANVAS_PIXELS2 / Math.max(pageWidth * pageHeight, 1)
|
|
1303
|
-
);
|
|
1304
|
-
const widthLimit = MAX_CANVAS_DIMENSION2 / Math.max(pageWidth, 1);
|
|
1305
|
-
const heightLimit = MAX_CANVAS_DIMENSION2 / Math.max(pageHeight, 1);
|
|
1306
|
-
const safeScale = Math.min(
|
|
1307
|
-
targetScale,
|
|
1308
|
-
Number.isFinite(areaLimit) ? areaLimit : targetScale,
|
|
1309
|
-
Number.isFinite(widthLimit) ? widthLimit : targetScale,
|
|
1310
|
-
Number.isFinite(heightLimit) ? heightLimit : targetScale
|
|
1311
|
-
);
|
|
1312
|
-
return Math.max(safeScale, 0);
|
|
1313
|
-
},
|
|
1314
|
-
[]
|
|
1315
|
-
);
|
|
1316
1493
|
useLayoutEffect(() => {
|
|
1317
1494
|
const scrollContainer = viewportRef?.current;
|
|
1318
1495
|
if (!scrollContainer) {
|
|
1319
1496
|
return;
|
|
1320
1497
|
}
|
|
1321
|
-
const detailCanvas =
|
|
1498
|
+
const detailCanvas = detailCanvasRef.current;
|
|
1322
1499
|
const container = containerRef.current;
|
|
1323
1500
|
if (!detailCanvas || !container) {
|
|
1324
1501
|
return;
|
|
1325
1502
|
}
|
|
1326
1503
|
let renderingTask = null;
|
|
1327
1504
|
let renderTimeoutId = null;
|
|
1505
|
+
const bgColor = background ?? "white";
|
|
1506
|
+
const detailBaseStyle = `position:absolute;top:0;left:0;pointer-events:none;z-index:1;background-color:${bgColor}`;
|
|
1328
1507
|
const hideDetailCanvas = () => {
|
|
1329
1508
|
renderingTask?.cancel();
|
|
1330
|
-
detailCanvas.style.
|
|
1331
|
-
|
|
1332
|
-
container.style.transform = "";
|
|
1333
|
-
container.style.transformOrigin = "";
|
|
1509
|
+
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:0`;
|
|
1510
|
+
container.style.cssText = "";
|
|
1334
1511
|
};
|
|
1335
1512
|
const renderDetailCanvas = () => {
|
|
1336
1513
|
const pageContainer = baseCanvasRef.current?.parentElement ?? null;
|
|
@@ -1374,27 +1551,19 @@ var useDetailCanvasLayer = ({
|
|
|
1374
1551
|
return;
|
|
1375
1552
|
}
|
|
1376
1553
|
renderingTask?.cancel();
|
|
1377
|
-
detailCanvas.style.display = "block";
|
|
1378
|
-
detailCanvas.style.opacity = "0";
|
|
1379
1554
|
const pdfOffsetX = visibleLeft;
|
|
1380
1555
|
const pdfOffsetY = visibleTop;
|
|
1381
1556
|
const effectiveScale = targetDetailScale;
|
|
1382
1557
|
const actualWidth = visibleWidth * effectiveScale;
|
|
1383
1558
|
const actualHeight = visibleHeight * effectiveScale;
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
detailCanvas.style.transform = `translate(${visibleLeft * zoom}px, ${visibleTop * zoom}px)`;
|
|
1390
|
-
container.style.transform = `scale3d(${1 / zoom}, ${1 / zoom}, 1)`;
|
|
1391
|
-
container.style.transformOrigin = "0 0";
|
|
1392
|
-
const context = detailCanvas.getContext("2d");
|
|
1393
|
-
if (!context) {
|
|
1559
|
+
const buffer = document.createElement("canvas");
|
|
1560
|
+
buffer.width = actualWidth;
|
|
1561
|
+
buffer.height = actualHeight;
|
|
1562
|
+
const bufferCtx = buffer.getContext("2d");
|
|
1563
|
+
if (!bufferCtx) {
|
|
1394
1564
|
return;
|
|
1395
1565
|
}
|
|
1396
|
-
|
|
1397
|
-
context.clearRect(0, 0, detailCanvas.width, detailCanvas.height);
|
|
1566
|
+
bufferCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1398
1567
|
const transform = [
|
|
1399
1568
|
1,
|
|
1400
1569
|
0,
|
|
@@ -1407,16 +1576,22 @@ var useDetailCanvasLayer = ({
|
|
|
1407
1576
|
scale: effectiveScale
|
|
1408
1577
|
});
|
|
1409
1578
|
const currentRenderingTask = pdfPageProxy.render({
|
|
1410
|
-
canvas:
|
|
1411
|
-
canvasContext:
|
|
1579
|
+
canvas: buffer,
|
|
1580
|
+
canvasContext: bufferCtx,
|
|
1412
1581
|
viewport: detailViewport,
|
|
1413
1582
|
background,
|
|
1414
1583
|
transform
|
|
1415
1584
|
});
|
|
1416
1585
|
renderingTask = currentRenderingTask;
|
|
1417
1586
|
currentRenderingTask.promise.then(() => {
|
|
1418
|
-
if (renderingTask
|
|
1419
|
-
|
|
1587
|
+
if (renderingTask !== currentRenderingTask) return;
|
|
1588
|
+
detailCanvas.width = actualWidth;
|
|
1589
|
+
detailCanvas.height = actualHeight;
|
|
1590
|
+
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:1;width:${visibleWidth * zoom}px;height:${visibleHeight * zoom}px;transform-origin:center center;transform:translate(${visibleLeft * zoom}px,${visibleTop * zoom}px)`;
|
|
1591
|
+
container.style.cssText = `transform:scale3d(${1 / zoom},${1 / zoom},1);transform-origin:0 0`;
|
|
1592
|
+
const ctx = detailCanvas.getContext("2d");
|
|
1593
|
+
if (ctx) {
|
|
1594
|
+
ctx.drawImage(buffer, 0, 0);
|
|
1420
1595
|
}
|
|
1421
1596
|
}).catch((error) => {
|
|
1422
1597
|
if (error.name === "RenderingCancelledException") {
|
|
@@ -1427,13 +1602,15 @@ var useDetailCanvasLayer = ({
|
|
|
1427
1602
|
if (renderingTask === currentRenderingTask) {
|
|
1428
1603
|
renderingTask = null;
|
|
1429
1604
|
}
|
|
1605
|
+
buffer.width = 0;
|
|
1606
|
+
buffer.height = 0;
|
|
1430
1607
|
});
|
|
1431
1608
|
};
|
|
1432
1609
|
const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
|
|
1433
1610
|
if (renderTimeoutId !== null) {
|
|
1434
1611
|
clearTimeout(renderTimeoutId);
|
|
1435
1612
|
}
|
|
1436
|
-
|
|
1613
|
+
renderingTask?.cancel();
|
|
1437
1614
|
renderTimeoutId = setTimeout(() => {
|
|
1438
1615
|
renderTimeoutId = null;
|
|
1439
1616
|
renderDetailCanvas();
|
|
@@ -1443,7 +1620,11 @@ var useDetailCanvasLayer = ({
|
|
|
1443
1620
|
scrollContainer,
|
|
1444
1621
|
scheduleRender
|
|
1445
1622
|
);
|
|
1446
|
-
|
|
1623
|
+
if (zoom <= 1 || isPinching) {
|
|
1624
|
+
hideDetailCanvas();
|
|
1625
|
+
} else {
|
|
1626
|
+
scheduleRender(0);
|
|
1627
|
+
}
|
|
1447
1628
|
return () => {
|
|
1448
1629
|
unsubscribe();
|
|
1449
1630
|
if (renderTimeoutId !== null) {
|
|
@@ -1458,10 +1639,17 @@ var useDetailCanvasLayer = ({
|
|
|
1458
1639
|
background,
|
|
1459
1640
|
dpr,
|
|
1460
1641
|
viewportRef,
|
|
1461
|
-
getDetailCanvas,
|
|
1462
|
-
clampScaleForPage,
|
|
1463
1642
|
baseCanvasRef
|
|
1464
1643
|
]);
|
|
1644
|
+
useEffect(() => {
|
|
1645
|
+
const canvas = detailCanvasRef.current;
|
|
1646
|
+
return () => {
|
|
1647
|
+
if (canvas) {
|
|
1648
|
+
canvas.width = 1;
|
|
1649
|
+
canvas.height = 1;
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}, []);
|
|
1465
1653
|
return {
|
|
1466
1654
|
detailCanvasRef,
|
|
1467
1655
|
containerRef
|
|
@@ -2201,6 +2389,10 @@ var ColoredHighlightLayer = ({
|
|
|
2201
2389
|
(state) => state.coloredHighlights
|
|
2202
2390
|
);
|
|
2203
2391
|
const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
|
|
2392
|
+
const pageHighlights = useMemo(
|
|
2393
|
+
() => highlights.filter((s) => s.pageNumber === pageNumber),
|
|
2394
|
+
[highlights, pageNumber]
|
|
2395
|
+
);
|
|
2204
2396
|
const handleHighlighting = useCallback(
|
|
2205
2397
|
(color) => {
|
|
2206
2398
|
const dimension = getDimension();
|
|
@@ -2222,13 +2414,7 @@ var ColoredHighlightLayer = ({
|
|
|
2222
2414
|
[onHighlight, getDimension, addColoredHighlight]
|
|
2223
2415
|
);
|
|
2224
2416
|
return /* @__PURE__ */ jsxs("div", { className: "colored-highlights-layer", children: [
|
|
2225
|
-
|
|
2226
|
-
ColoredHighlightComponent,
|
|
2227
|
-
{
|
|
2228
|
-
selection
|
|
2229
|
-
},
|
|
2230
|
-
selection.uuid
|
|
2231
|
-
)),
|
|
2417
|
+
pageHighlights.map((selection) => /* @__PURE__ */ jsx(ColoredHighlightComponent, { selection }, selection.uuid)),
|
|
2232
2418
|
/* @__PURE__ */ jsx(
|
|
2233
2419
|
ColorSelectionTool,
|
|
2234
2420
|
{
|
|
@@ -2256,7 +2442,10 @@ var HighlightLayer = forwardRef(({ asChild, className, style, ...props }, ref) =
|
|
|
2256
2442
|
const pageNumber = usePDFPageNumber();
|
|
2257
2443
|
const highlights = usePdf((state) => state.highlights);
|
|
2258
2444
|
const Comp = asChild ? Slot : "div";
|
|
2259
|
-
const rects =
|
|
2445
|
+
const rects = useMemo(
|
|
2446
|
+
() => highlights.filter((area) => area.pageNumber === pageNumber),
|
|
2447
|
+
[highlights, pageNumber]
|
|
2448
|
+
);
|
|
2260
2449
|
if (!rects?.length) return null;
|
|
2261
2450
|
return /* @__PURE__ */ jsx(Fragment, { children: rects.map((rect, index) => {
|
|
2262
2451
|
const { pageNumber: pageNumber2, type, style: customStyle, ...coordinates } = rect;
|
|
@@ -2831,6 +3020,7 @@ var useVisiblePage = ({ items }) => {
|
|
|
2831
3020
|
const isPinching = usePdf((state) => state.isPinching);
|
|
2832
3021
|
const setCurrentPage = usePdf((state) => state.setCurrentPage);
|
|
2833
3022
|
const scrollElement = usePdf((state) => state.viewportRef?.current);
|
|
3023
|
+
const lastPageRef = useRef(0);
|
|
2834
3024
|
const calculateVisiblePageIndex = useCallback(
|
|
2835
3025
|
(virtualItems) => {
|
|
2836
3026
|
if (!scrollElement || virtualItems.length === 0) return 0;
|
|
@@ -2854,7 +3044,11 @@ var useVisiblePage = ({ items }) => {
|
|
|
2854
3044
|
useEffect(() => {
|
|
2855
3045
|
if (!isPinching && items.length > 0) {
|
|
2856
3046
|
const mostVisibleIndex = calculateVisiblePageIndex(items);
|
|
2857
|
-
|
|
3047
|
+
const page = mostVisibleIndex + 1;
|
|
3048
|
+
if (page !== lastPageRef.current) {
|
|
3049
|
+
lastPageRef.current = page;
|
|
3050
|
+
setCurrentPage?.(page);
|
|
3051
|
+
}
|
|
2858
3052
|
}
|
|
2859
3053
|
}, [items, isPinching, calculateVisiblePageIndex, setCurrentPage]);
|
|
2860
3054
|
return null;
|
|
@@ -4929,8 +5123,10 @@ var useViewportContainer = ({
|
|
|
4929
5123
|
origin
|
|
4930
5124
|
};
|
|
4931
5125
|
};
|
|
5126
|
+
var selectLargestPageWidth = (state) => state.viewports.reduce((max, vp) => Math.max(max, vp.width), 0);
|
|
4932
5127
|
var DEFAULT_HEIGHT = 600;
|
|
4933
5128
|
var EXTRA_HEIGHT = 0;
|
|
5129
|
+
var DEFAULT_VIRTUALIZER_OPTIONS = { overscan: 1 };
|
|
4934
5130
|
var VirtualizedPageItem = memo(
|
|
4935
5131
|
({ child, virtualItem, innerBoxWidth }) => {
|
|
4936
5132
|
return /* @__PURE__ */ jsx(
|
|
@@ -4963,7 +5159,7 @@ VirtualizedPageItem.displayName = "VirtualizedPageItem";
|
|
|
4963
5159
|
var Pages = ({
|
|
4964
5160
|
children,
|
|
4965
5161
|
gap = 10,
|
|
4966
|
-
virtualizerOptions =
|
|
5162
|
+
virtualizerOptions = DEFAULT_VIRTUALIZER_OPTIONS,
|
|
4967
5163
|
initialOffset,
|
|
4968
5164
|
onOffsetChange,
|
|
4969
5165
|
...props
|
|
@@ -4983,12 +5179,16 @@ var Pages = ({
|
|
|
4983
5179
|
const setVirtualizer = usePdf((state) => state.setVirtualizer);
|
|
4984
5180
|
const { scrollToFn } = useScrollFn();
|
|
4985
5181
|
const { observeElementOffset } = useObserveElement();
|
|
5182
|
+
const viewportsRef = useRef(viewports);
|
|
5183
|
+
viewportsRef.current = viewports;
|
|
4986
5184
|
const estimateSize = useCallback(
|
|
4987
5185
|
(index) => {
|
|
4988
|
-
|
|
4989
|
-
|
|
5186
|
+
const vp = viewportsRef.current;
|
|
5187
|
+
if (!vp || !vp[index]) return DEFAULT_HEIGHT;
|
|
5188
|
+
return vp[index].height + EXTRA_HEIGHT;
|
|
4990
5189
|
},
|
|
4991
|
-
[
|
|
5190
|
+
[]
|
|
5191
|
+
// Stable — reads from ref
|
|
4992
5192
|
);
|
|
4993
5193
|
const virtualizer = useVirtualizer({
|
|
4994
5194
|
count: numPages || 0,
|
|
@@ -5028,11 +5228,7 @@ var Pages = ({
|
|
|
5028
5228
|
items
|
|
5029
5229
|
});
|
|
5030
5230
|
useFitWidth({ viewportRef: containerRef });
|
|
5031
|
-
const largestPageWidth = usePdf(
|
|
5032
|
-
(state) => state.viewports.reduce((largestWidth, viewport) => {
|
|
5033
|
-
return Math.max(largestWidth, viewport.width);
|
|
5034
|
-
}, 0)
|
|
5035
|
-
);
|
|
5231
|
+
const largestPageWidth = usePdf(selectLargestPageWidth);
|
|
5036
5232
|
useEffect(() => {
|
|
5037
5233
|
virtualizer.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
|
|
5038
5234
|
const size = virtualizer.getSize();
|
|
@@ -5108,17 +5304,44 @@ var Pages = ({
|
|
|
5108
5304
|
}
|
|
5109
5305
|
);
|
|
5110
5306
|
};
|
|
5307
|
+
function buildDocumentInitParams(source, overrides) {
|
|
5308
|
+
let params;
|
|
5309
|
+
if (typeof source === "string" || source instanceof URL) {
|
|
5310
|
+
params = { url: source };
|
|
5311
|
+
} else if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) {
|
|
5312
|
+
params = { data: source };
|
|
5313
|
+
} else {
|
|
5314
|
+
params = { ...source };
|
|
5315
|
+
}
|
|
5316
|
+
const defaults = {
|
|
5317
|
+
// Skip the slow _guessMax binary-search auto-detection of max canvas area.
|
|
5318
|
+
// 1 GiB = 256 megapixels at 4 bytes/pixel. If the device can't allocate
|
|
5319
|
+
// this, pdfjs falls back gracefully via try/catch.
|
|
5320
|
+
canvasMaxAreaInBytes: 1024 * 1024 * 1024,
|
|
5321
|
+
// Enable GPU-backed canvases (willReadFrequently: false). PDF viewers are
|
|
5322
|
+
// render-dominated — they don't call getImageData() — so GPU compositing
|
|
5323
|
+
// is the correct path. This is the browser's default when no hint is given.
|
|
5324
|
+
enableHWA: true,
|
|
5325
|
+
// Explicitly opt-in to OffscreenCanvas in the worker for image processing.
|
|
5326
|
+
// Already the browser default, but being explicit avoids edge cases.
|
|
5327
|
+
isOffscreenCanvasSupported: true
|
|
5328
|
+
};
|
|
5329
|
+
return { ...defaults, ...params, ...overrides };
|
|
5330
|
+
}
|
|
5111
5331
|
var usePDFDocumentContext = ({
|
|
5112
5332
|
onDocumentLoad,
|
|
5113
5333
|
source,
|
|
5114
5334
|
initialRotation = 0,
|
|
5115
5335
|
isZoomFitWidth,
|
|
5116
5336
|
zoom = 1,
|
|
5117
|
-
zoomOptions
|
|
5337
|
+
zoomOptions,
|
|
5338
|
+
documentOptions
|
|
5118
5339
|
}) => {
|
|
5119
5340
|
const [_, setProgress] = useState(0);
|
|
5120
5341
|
const [initialState, setInitialState] = useState();
|
|
5121
5342
|
const [rotation] = useState(initialRotation);
|
|
5343
|
+
const documentOptionsRef = useRef(documentOptions);
|
|
5344
|
+
documentOptionsRef.current = documentOptions;
|
|
5122
5345
|
useEffect(() => {
|
|
5123
5346
|
const generateViewports = async (pdf) => {
|
|
5124
5347
|
const pageProxies = [];
|
|
@@ -5136,9 +5359,9 @@ var usePDFDocumentContext = ({
|
|
|
5136
5359
|
return viewport;
|
|
5137
5360
|
})
|
|
5138
5361
|
);
|
|
5139
|
-
const sortedPageProxies = pageProxies.
|
|
5140
|
-
|
|
5141
|
-
|
|
5362
|
+
const sortedPageProxies = pageProxies.toSorted(
|
|
5363
|
+
(a, b) => a.pageNumber - b.pageNumber
|
|
5364
|
+
);
|
|
5142
5365
|
setInitialState((prev) => ({
|
|
5143
5366
|
...prev,
|
|
5144
5367
|
isZoomFitWidth,
|
|
@@ -5158,7 +5381,9 @@ var usePDFDocumentContext = ({
|
|
|
5158
5381
|
if (isDisposed) {
|
|
5159
5382
|
return;
|
|
5160
5383
|
}
|
|
5161
|
-
loadingTask = getDocument(
|
|
5384
|
+
loadingTask = getDocument(
|
|
5385
|
+
buildDocumentInitParams(source, documentOptionsRef.current)
|
|
5386
|
+
);
|
|
5162
5387
|
loadingTask.onProgress = (progressEvent) => {
|
|
5163
5388
|
if (progressEvent.loaded === progressEvent.total) {
|
|
5164
5389
|
return;
|
|
@@ -5204,6 +5429,7 @@ var Root = forwardRef(
|
|
|
5204
5429
|
isZoomFitWidth,
|
|
5205
5430
|
zoom,
|
|
5206
5431
|
zoomOptions,
|
|
5432
|
+
documentOptions,
|
|
5207
5433
|
...props
|
|
5208
5434
|
}, ref) => {
|
|
5209
5435
|
const { initialState } = usePDFDocumentContext({
|
|
@@ -5211,12 +5437,20 @@ var Root = forwardRef(
|
|
|
5211
5437
|
onDocumentLoad,
|
|
5212
5438
|
isZoomFitWidth,
|
|
5213
5439
|
zoom,
|
|
5214
|
-
zoomOptions
|
|
5440
|
+
zoomOptions,
|
|
5441
|
+
documentOptions
|
|
5215
5442
|
});
|
|
5216
5443
|
const linkService = useCreatePDFLinkService(
|
|
5217
5444
|
initialState?.pdfDocumentProxy ?? null
|
|
5218
5445
|
);
|
|
5219
|
-
|
|
5446
|
+
const documentId = initialState?.pdfDocumentProxy.fingerprints[0];
|
|
5447
|
+
useEffect(() => {
|
|
5448
|
+
if (!documentId) return;
|
|
5449
|
+
return () => {
|
|
5450
|
+
renderCache.invalidateDocument(documentId);
|
|
5451
|
+
};
|
|
5452
|
+
}, [documentId]);
|
|
5453
|
+
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..." });
|
|
5220
5454
|
}
|
|
5221
5455
|
);
|
|
5222
5456
|
Root.displayName = "Root";
|
|
@@ -5266,8 +5500,8 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5266
5500
|
const isVisible = isFirstPage || debouncedVisible;
|
|
5267
5501
|
useEffect(() => {
|
|
5268
5502
|
const renderThumbnail = async () => {
|
|
5269
|
-
const
|
|
5270
|
-
if (!
|
|
5503
|
+
const canvas2 = canvasRef.current;
|
|
5504
|
+
if (!canvas2 || !pageProxy) return;
|
|
5271
5505
|
try {
|
|
5272
5506
|
if (renderTaskRef.current) {
|
|
5273
5507
|
renderTaskRef.current.cancel();
|
|
@@ -5275,12 +5509,12 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5275
5509
|
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
5276
5510
|
const scale = Math.min(maxWidth / viewport.width, maxHeight / viewport.height) * (isVisible ? dpr : 0.5);
|
|
5277
5511
|
const scaledViewport = pageProxy.getViewport({ scale });
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
const context =
|
|
5512
|
+
canvas2.width = scaledViewport.width;
|
|
5513
|
+
canvas2.height = scaledViewport.height;
|
|
5514
|
+
const context = canvas2.getContext("2d");
|
|
5281
5515
|
if (!context) return;
|
|
5282
5516
|
const renderTask = pageProxy.render({
|
|
5283
|
-
canvas,
|
|
5517
|
+
canvas: canvas2,
|
|
5284
5518
|
canvasContext: context,
|
|
5285
5519
|
viewport: scaledViewport
|
|
5286
5520
|
});
|
|
@@ -5295,8 +5529,13 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5295
5529
|
}
|
|
5296
5530
|
};
|
|
5297
5531
|
renderThumbnail();
|
|
5532
|
+
const canvas = canvasRef.current;
|
|
5298
5533
|
return () => {
|
|
5299
5534
|
renderTaskRef.current?.cancel();
|
|
5535
|
+
if (canvas) {
|
|
5536
|
+
canvas.width = 1;
|
|
5537
|
+
canvas.height = 1;
|
|
5538
|
+
}
|
|
5300
5539
|
};
|
|
5301
5540
|
}, [pageProxy, isVisible, dpr, maxHeight, maxWidth]);
|
|
5302
5541
|
return {
|
|
@@ -5375,12 +5614,12 @@ var ZoomOut = ({ ...props }) => {
|
|
|
5375
5614
|
);
|
|
5376
5615
|
};
|
|
5377
5616
|
var CurrentZoom = ({ ...props }) => {
|
|
5378
|
-
const
|
|
5617
|
+
const updateZoom = usePdf((state) => state.updateZoom);
|
|
5379
5618
|
const realZoom = usePdf((state) => state.zoom);
|
|
5380
5619
|
const [zoom, setZoom] = useState((realZoom * 100).toFixed(0));
|
|
5381
|
-
const
|
|
5620
|
+
const isInputFocused = useRef(false);
|
|
5382
5621
|
useEffect(() => {
|
|
5383
|
-
if (
|
|
5622
|
+
if (isInputFocused.current) {
|
|
5384
5623
|
return;
|
|
5385
5624
|
}
|
|
5386
5625
|
setZoom((realZoom * 100).toFixed(0));
|
|
@@ -5391,14 +5630,14 @@ var CurrentZoom = ({ ...props }) => {
|
|
|
5391
5630
|
...props,
|
|
5392
5631
|
value: zoom,
|
|
5393
5632
|
onClick: () => {
|
|
5394
|
-
|
|
5633
|
+
isInputFocused.current = true;
|
|
5395
5634
|
},
|
|
5396
5635
|
onChange: (e) => {
|
|
5397
|
-
|
|
5636
|
+
updateZoom(Number(e.target.value) / 100);
|
|
5398
5637
|
setZoom(e.target.value);
|
|
5399
5638
|
},
|
|
5400
5639
|
onBlur: () => {
|
|
5401
|
-
|
|
5640
|
+
isInputFocused.current = false;
|
|
5402
5641
|
setZoom((realZoom * 100).toFixed(0));
|
|
5403
5642
|
}
|
|
5404
5643
|
}
|
|
@@ -5631,4 +5870,7 @@ function mergeAdjacentRects(rects) {
|
|
|
5631
5870
|
return merged;
|
|
5632
5871
|
}
|
|
5633
5872
|
|
|
5634
|
-
|
|
5873
|
+
// src/hooks/usePageRendered.ts
|
|
5874
|
+
var usePageRendered = (pageNumber) => usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
5875
|
+
|
|
5876
|
+
export { AnnotationHighlightLayer, AnnotationLayer, AnnotationTooltip, CanvasLayer, ColoredHighlightLayer, CurrentPage, CurrentZoom, CustomLayer, HighlightLayer, LinkService, NextPage, Outline, OutlineChildItems, OutlineItem, PDFLinkServiceContext, Page, Pages, PreviousPage, Root, Search, SelectionTooltip, TextLayer, Thumbnail, Thumbnails, TotalPages, ZoomIn, ZoomOut, calculateHighlightRects, useAnnotations, useCreatePDFLinkService, usePDFLinkService, usePDFPageNumber, usePageRendered, usePdf, usePdfJump, useSearch, useSelectionDimensions };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anaralabs/lector",
|
|
3
|
-
"version": "3.10.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.10.1",
|
|
4
|
+
"description": "A headless PDF viewer for React",
|
|
5
5
|
"author": "andrewdr (https://github.com/andrewdoro)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|