@anaralabs/lector 3.10.0 → 3.10.2
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 +453 -178
- 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 = 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();
|
|
1102
1257
|
var useDpr = () => {
|
|
1103
1258
|
const [dpr, setDPR] = useState(
|
|
1104
1259
|
typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
|
|
@@ -1122,79 +1277,143 @@ 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);
|
|
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
|
+
}
|
|
1154
1304
|
useLayoutEffect(() => {
|
|
1155
1305
|
if (!canvasRef.current) {
|
|
1156
1306
|
return;
|
|
1157
1307
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
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";
|
|
1162
1315
|
const targetBaseScale = dpr * Math.min(zoom, 1);
|
|
1163
1316
|
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) {
|
|
1317
|
+
if (baseScale === lastRenderedScaleRef.current) {
|
|
1177
1318
|
return;
|
|
1178
1319
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const
|
|
1182
|
-
const
|
|
1183
|
-
canvas
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
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);
|
|
1191
1332
|
}
|
|
1192
|
-
|
|
1333
|
+
lastRenderedScaleRef.current = baseScale;
|
|
1334
|
+
};
|
|
1335
|
+
const cached = renderCache.get(
|
|
1336
|
+
documentId,
|
|
1337
|
+
pageNumber,
|
|
1338
|
+
baseScale,
|
|
1339
|
+
background
|
|
1340
|
+
);
|
|
1341
|
+
if (cached) {
|
|
1342
|
+
swapToCanvas(cached.bitmap, cached.width, cached.height);
|
|
1343
|
+
markPageRendered(pageNumber);
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
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();
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
reject(error);
|
|
1390
|
+
});
|
|
1391
|
+
});
|
|
1193
1392
|
});
|
|
1194
1393
|
return () => {
|
|
1195
|
-
|
|
1394
|
+
cancelled = true;
|
|
1395
|
+
job.cancel();
|
|
1396
|
+
void activeRenderingTask?.cancel();
|
|
1397
|
+
};
|
|
1398
|
+
}, [
|
|
1399
|
+
pdfPageProxy,
|
|
1400
|
+
background,
|
|
1401
|
+
dpr,
|
|
1402
|
+
zoom,
|
|
1403
|
+
pageNumber,
|
|
1404
|
+
documentId,
|
|
1405
|
+
markPageRendered
|
|
1406
|
+
]);
|
|
1407
|
+
useEffect(() => {
|
|
1408
|
+
const canvas = canvasRef.current;
|
|
1409
|
+
return () => {
|
|
1410
|
+
unmarkPageRendered(pageNumber);
|
|
1411
|
+
if (canvas) {
|
|
1412
|
+
canvas.width = 1;
|
|
1413
|
+
canvas.height = 1;
|
|
1414
|
+
}
|
|
1196
1415
|
};
|
|
1197
|
-
}, [
|
|
1416
|
+
}, [pageNumber, unmarkPageRendered]);
|
|
1198
1417
|
return {
|
|
1199
1418
|
canvasRef
|
|
1200
1419
|
};
|
|
@@ -1264,8 +1483,6 @@ var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
|
1264
1483
|
};
|
|
1265
1484
|
|
|
1266
1485
|
// src/hooks/layers/useDetailCanvasLayer.tsx
|
|
1267
|
-
var MAX_CANVAS_PIXELS2 = 16777216;
|
|
1268
|
-
var MAX_CANVAS_DIMENSION2 = 32767;
|
|
1269
1486
|
var DETAIL_RENDER_IDLE_MS = 80;
|
|
1270
1487
|
var useDetailCanvasLayer = ({
|
|
1271
1488
|
background,
|
|
@@ -1280,57 +1497,33 @@ var useDetailCanvasLayer = ({
|
|
|
1280
1497
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1281
1498
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1282
1499
|
const [zoom] = useDebounce(bouncyZoom, 200);
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
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
|
-
);
|
|
1500
|
+
const pageDimsRef = useRef(null);
|
|
1501
|
+
if (!pageDimsRef.current || pageDimsRef.current.proxy !== pdfPageProxy) {
|
|
1502
|
+
const vp = pdfPageProxy.getViewport({ scale: 1 });
|
|
1503
|
+
pageDimsRef.current = {
|
|
1504
|
+
width: vp.width,
|
|
1505
|
+
height: vp.height,
|
|
1506
|
+
proxy: pdfPageProxy
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1316
1509
|
useLayoutEffect(() => {
|
|
1317
1510
|
const scrollContainer = viewportRef?.current;
|
|
1318
1511
|
if (!scrollContainer) {
|
|
1319
1512
|
return;
|
|
1320
1513
|
}
|
|
1321
|
-
const detailCanvas =
|
|
1514
|
+
const detailCanvas = detailCanvasRef.current;
|
|
1322
1515
|
const container = containerRef.current;
|
|
1323
1516
|
if (!detailCanvas || !container) {
|
|
1324
1517
|
return;
|
|
1325
1518
|
}
|
|
1326
1519
|
let renderingTask = null;
|
|
1327
1520
|
let renderTimeoutId = null;
|
|
1521
|
+
const bgColor = background ?? "white";
|
|
1522
|
+
const detailBaseStyle = `position:absolute;top:0;left:0;pointer-events:none;z-index:1;background-color:${bgColor}`;
|
|
1328
1523
|
const hideDetailCanvas = () => {
|
|
1329
1524
|
renderingTask?.cancel();
|
|
1330
|
-
detailCanvas.style.
|
|
1331
|
-
|
|
1332
|
-
container.style.transform = "";
|
|
1333
|
-
container.style.transformOrigin = "";
|
|
1525
|
+
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:0`;
|
|
1526
|
+
container.style.cssText = "";
|
|
1334
1527
|
};
|
|
1335
1528
|
const renderDetailCanvas = () => {
|
|
1336
1529
|
const pageContainer = baseCanvasRef.current?.parentElement ?? null;
|
|
@@ -1338,9 +1531,7 @@ var useDetailCanvasLayer = ({
|
|
|
1338
1531
|
hideDetailCanvas();
|
|
1339
1532
|
return;
|
|
1340
1533
|
}
|
|
1341
|
-
const
|
|
1342
|
-
const pageWidth = baseViewport.width;
|
|
1343
|
-
const pageHeight = baseViewport.height;
|
|
1534
|
+
const { width: pageWidth, height: pageHeight } = pageDimsRef.current;
|
|
1344
1535
|
const scrollX = scrollContainer.scrollLeft / zoom;
|
|
1345
1536
|
const scrollY = scrollContainer.scrollTop / zoom;
|
|
1346
1537
|
const viewportWidth = scrollContainer.clientWidth / zoom;
|
|
@@ -1374,27 +1565,19 @@ var useDetailCanvasLayer = ({
|
|
|
1374
1565
|
return;
|
|
1375
1566
|
}
|
|
1376
1567
|
renderingTask?.cancel();
|
|
1377
|
-
detailCanvas.style.display = "block";
|
|
1378
|
-
detailCanvas.style.opacity = "0";
|
|
1379
1568
|
const pdfOffsetX = visibleLeft;
|
|
1380
1569
|
const pdfOffsetY = visibleTop;
|
|
1381
1570
|
const effectiveScale = targetDetailScale;
|
|
1382
1571
|
const actualWidth = visibleWidth * effectiveScale;
|
|
1383
1572
|
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) {
|
|
1573
|
+
const buffer = document.createElement("canvas");
|
|
1574
|
+
buffer.width = actualWidth;
|
|
1575
|
+
buffer.height = actualHeight;
|
|
1576
|
+
const bufferCtx = buffer.getContext("2d");
|
|
1577
|
+
if (!bufferCtx) {
|
|
1394
1578
|
return;
|
|
1395
1579
|
}
|
|
1396
|
-
|
|
1397
|
-
context.clearRect(0, 0, detailCanvas.width, detailCanvas.height);
|
|
1580
|
+
bufferCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1398
1581
|
const transform = [
|
|
1399
1582
|
1,
|
|
1400
1583
|
0,
|
|
@@ -1407,16 +1590,22 @@ var useDetailCanvasLayer = ({
|
|
|
1407
1590
|
scale: effectiveScale
|
|
1408
1591
|
});
|
|
1409
1592
|
const currentRenderingTask = pdfPageProxy.render({
|
|
1410
|
-
canvas:
|
|
1411
|
-
canvasContext:
|
|
1593
|
+
canvas: buffer,
|
|
1594
|
+
canvasContext: bufferCtx,
|
|
1412
1595
|
viewport: detailViewport,
|
|
1413
1596
|
background,
|
|
1414
1597
|
transform
|
|
1415
1598
|
});
|
|
1416
1599
|
renderingTask = currentRenderingTask;
|
|
1417
1600
|
currentRenderingTask.promise.then(() => {
|
|
1418
|
-
if (renderingTask
|
|
1419
|
-
|
|
1601
|
+
if (renderingTask !== currentRenderingTask) return;
|
|
1602
|
+
detailCanvas.width = actualWidth;
|
|
1603
|
+
detailCanvas.height = actualHeight;
|
|
1604
|
+
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)`;
|
|
1605
|
+
container.style.cssText = `transform:scale3d(${1 / zoom},${1 / zoom},1);transform-origin:0 0`;
|
|
1606
|
+
const ctx = detailCanvas.getContext("2d");
|
|
1607
|
+
if (ctx) {
|
|
1608
|
+
ctx.drawImage(buffer, 0, 0);
|
|
1420
1609
|
}
|
|
1421
1610
|
}).catch((error) => {
|
|
1422
1611
|
if (error.name === "RenderingCancelledException") {
|
|
@@ -1427,13 +1616,15 @@ var useDetailCanvasLayer = ({
|
|
|
1427
1616
|
if (renderingTask === currentRenderingTask) {
|
|
1428
1617
|
renderingTask = null;
|
|
1429
1618
|
}
|
|
1619
|
+
buffer.width = 0;
|
|
1620
|
+
buffer.height = 0;
|
|
1430
1621
|
});
|
|
1431
1622
|
};
|
|
1432
1623
|
const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
|
|
1433
1624
|
if (renderTimeoutId !== null) {
|
|
1434
1625
|
clearTimeout(renderTimeoutId);
|
|
1435
1626
|
}
|
|
1436
|
-
|
|
1627
|
+
renderingTask?.cancel();
|
|
1437
1628
|
renderTimeoutId = setTimeout(() => {
|
|
1438
1629
|
renderTimeoutId = null;
|
|
1439
1630
|
renderDetailCanvas();
|
|
@@ -1443,7 +1634,11 @@ var useDetailCanvasLayer = ({
|
|
|
1443
1634
|
scrollContainer,
|
|
1444
1635
|
scheduleRender
|
|
1445
1636
|
);
|
|
1446
|
-
|
|
1637
|
+
if (zoom <= 1 || isPinching) {
|
|
1638
|
+
hideDetailCanvas();
|
|
1639
|
+
} else {
|
|
1640
|
+
scheduleRender(0);
|
|
1641
|
+
}
|
|
1447
1642
|
return () => {
|
|
1448
1643
|
unsubscribe();
|
|
1449
1644
|
if (renderTimeoutId !== null) {
|
|
@@ -1458,10 +1653,17 @@ var useDetailCanvasLayer = ({
|
|
|
1458
1653
|
background,
|
|
1459
1654
|
dpr,
|
|
1460
1655
|
viewportRef,
|
|
1461
|
-
getDetailCanvas,
|
|
1462
|
-
clampScaleForPage,
|
|
1463
1656
|
baseCanvasRef
|
|
1464
1657
|
]);
|
|
1658
|
+
useEffect(() => {
|
|
1659
|
+
const canvas = detailCanvasRef.current;
|
|
1660
|
+
return () => {
|
|
1661
|
+
if (canvas) {
|
|
1662
|
+
canvas.width = 1;
|
|
1663
|
+
canvas.height = 1;
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
}, []);
|
|
1465
1667
|
return {
|
|
1466
1668
|
detailCanvasRef,
|
|
1467
1669
|
containerRef
|
|
@@ -2201,6 +2403,10 @@ var ColoredHighlightLayer = ({
|
|
|
2201
2403
|
(state) => state.coloredHighlights
|
|
2202
2404
|
);
|
|
2203
2405
|
const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
|
|
2406
|
+
const pageHighlights = useMemo(
|
|
2407
|
+
() => highlights.filter((s) => s.pageNumber === pageNumber),
|
|
2408
|
+
[highlights, pageNumber]
|
|
2409
|
+
);
|
|
2204
2410
|
const handleHighlighting = useCallback(
|
|
2205
2411
|
(color) => {
|
|
2206
2412
|
const dimension = getDimension();
|
|
@@ -2222,13 +2428,7 @@ var ColoredHighlightLayer = ({
|
|
|
2222
2428
|
[onHighlight, getDimension, addColoredHighlight]
|
|
2223
2429
|
);
|
|
2224
2430
|
return /* @__PURE__ */ jsxs("div", { className: "colored-highlights-layer", children: [
|
|
2225
|
-
|
|
2226
|
-
ColoredHighlightComponent,
|
|
2227
|
-
{
|
|
2228
|
-
selection
|
|
2229
|
-
},
|
|
2230
|
-
selection.uuid
|
|
2231
|
-
)),
|
|
2431
|
+
pageHighlights.map((selection) => /* @__PURE__ */ jsx(ColoredHighlightComponent, { selection }, selection.uuid)),
|
|
2232
2432
|
/* @__PURE__ */ jsx(
|
|
2233
2433
|
ColorSelectionTool,
|
|
2234
2434
|
{
|
|
@@ -2256,7 +2456,10 @@ var HighlightLayer = forwardRef(({ asChild, className, style, ...props }, ref) =
|
|
|
2256
2456
|
const pageNumber = usePDFPageNumber();
|
|
2257
2457
|
const highlights = usePdf((state) => state.highlights);
|
|
2258
2458
|
const Comp = asChild ? Slot : "div";
|
|
2259
|
-
const rects =
|
|
2459
|
+
const rects = useMemo(
|
|
2460
|
+
() => highlights.filter((area) => area.pageNumber === pageNumber),
|
|
2461
|
+
[highlights, pageNumber]
|
|
2462
|
+
);
|
|
2260
2463
|
if (!rects?.length) return null;
|
|
2261
2464
|
return /* @__PURE__ */ jsx(Fragment, { children: rects.map((rect, index) => {
|
|
2262
2465
|
const { pageNumber: pageNumber2, type, style: customStyle, ...coordinates } = rect;
|
|
@@ -2831,6 +3034,7 @@ var useVisiblePage = ({ items }) => {
|
|
|
2831
3034
|
const isPinching = usePdf((state) => state.isPinching);
|
|
2832
3035
|
const setCurrentPage = usePdf((state) => state.setCurrentPage);
|
|
2833
3036
|
const scrollElement = usePdf((state) => state.viewportRef?.current);
|
|
3037
|
+
const lastPageRef = useRef(0);
|
|
2834
3038
|
const calculateVisiblePageIndex = useCallback(
|
|
2835
3039
|
(virtualItems) => {
|
|
2836
3040
|
if (!scrollElement || virtualItems.length === 0) return 0;
|
|
@@ -2854,7 +3058,11 @@ var useVisiblePage = ({ items }) => {
|
|
|
2854
3058
|
useEffect(() => {
|
|
2855
3059
|
if (!isPinching && items.length > 0) {
|
|
2856
3060
|
const mostVisibleIndex = calculateVisiblePageIndex(items);
|
|
2857
|
-
|
|
3061
|
+
const page = mostVisibleIndex + 1;
|
|
3062
|
+
if (page !== lastPageRef.current) {
|
|
3063
|
+
lastPageRef.current = page;
|
|
3064
|
+
setCurrentPage?.(page);
|
|
3065
|
+
}
|
|
2858
3066
|
}
|
|
2859
3067
|
}, [items, isPinching, calculateVisiblePageIndex, setCurrentPage]);
|
|
2860
3068
|
return null;
|
|
@@ -4736,7 +4944,7 @@ var useViewportContainer = ({
|
|
|
4736
4944
|
elementWrapperRef,
|
|
4737
4945
|
elementRef
|
|
4738
4946
|
}) => {
|
|
4739
|
-
const
|
|
4947
|
+
const originRef = useRef([0, 0]);
|
|
4740
4948
|
const wheelInertia = useRef({
|
|
4741
4949
|
active: false,
|
|
4742
4950
|
lastTime: 0,
|
|
@@ -4756,28 +4964,46 @@ var useViewportContainer = ({
|
|
|
4756
4964
|
translateY: 0,
|
|
4757
4965
|
zoom
|
|
4758
4966
|
});
|
|
4967
|
+
const zoomRafRef = useRef(null);
|
|
4968
|
+
const lastPushedZoomRef = useRef(zoom);
|
|
4759
4969
|
const updateTransform = useCallback(
|
|
4760
4970
|
(zoomUpdate) => {
|
|
4761
4971
|
if (!elementRef.current || !containerRef.current || !elementWrapperRef.current) {
|
|
4762
4972
|
return;
|
|
4763
4973
|
}
|
|
4764
4974
|
const { zoom: zoom2, translateX, translateY } = transformations.current;
|
|
4975
|
+
const naturalWidth = parseFloat(elementRef.current.style.width) || elementRef.current.offsetWidth;
|
|
4976
|
+
const naturalHeight = parseFloat(elementRef.current.style.height) || elementRef.current.offsetHeight;
|
|
4765
4977
|
elementRef.current.style.transform = `scale3d(${zoom2}, ${zoom2}, 1)`;
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
const width = elementBoundingBox.width;
|
|
4769
|
-
elementWrapperRef.current.style.width = `${width}px`;
|
|
4770
|
-
elementWrapperRef.current.style.height = `${elementBoundingBox.height}px`;
|
|
4978
|
+
elementWrapperRef.current.style.width = `${naturalWidth * zoom2}px`;
|
|
4979
|
+
elementWrapperRef.current.style.height = `${naturalHeight * zoom2}px`;
|
|
4771
4980
|
containerRef.current.scrollTop = translateY;
|
|
4772
4981
|
containerRef.current.scrollLeft = translateX;
|
|
4773
|
-
if (zoomUpdate
|
|
4982
|
+
if (zoomUpdate && zoomRafRef.current === null) {
|
|
4983
|
+
zoomRafRef.current = requestAnimationFrame(() => {
|
|
4984
|
+
zoomRafRef.current = null;
|
|
4985
|
+
const z = transformations.current.zoom;
|
|
4986
|
+
lastPushedZoomRef.current = z;
|
|
4987
|
+
updateZoom(() => z);
|
|
4988
|
+
});
|
|
4989
|
+
}
|
|
4774
4990
|
},
|
|
4775
4991
|
[containerRef, elementRef, elementWrapperRef, updateZoom]
|
|
4776
4992
|
);
|
|
4993
|
+
useEffect(() => {
|
|
4994
|
+
return () => {
|
|
4995
|
+
if (zoomRafRef.current !== null) {
|
|
4996
|
+
cancelAnimationFrame(zoomRafRef.current);
|
|
4997
|
+
}
|
|
4998
|
+
};
|
|
4999
|
+
}, []);
|
|
4777
5000
|
useEffect(() => {
|
|
4778
5001
|
if (transformations.current.zoom === zoom || !containerRef.current) {
|
|
4779
5002
|
return;
|
|
4780
5003
|
}
|
|
5004
|
+
if (zoom === lastPushedZoomRef.current) {
|
|
5005
|
+
return;
|
|
5006
|
+
}
|
|
4781
5007
|
const prevZoom = transformations.current.zoom;
|
|
4782
5008
|
if (!prevZoom || !Number.isFinite(prevZoom)) {
|
|
4783
5009
|
transformations.current = {
|
|
@@ -4856,7 +5082,7 @@ var useViewportContainer = ({
|
|
|
4856
5082
|
useGesture(
|
|
4857
5083
|
{
|
|
4858
5084
|
onPinch: (state) => {
|
|
4859
|
-
const { origin
|
|
5085
|
+
const { origin, first, movement, delta, event, memo: memo7 } = state;
|
|
4860
5086
|
const [ms] = movement;
|
|
4861
5087
|
const [deltaScale] = delta;
|
|
4862
5088
|
const currentElement = elementRef.current;
|
|
@@ -4867,17 +5093,17 @@ var useViewportContainer = ({
|
|
|
4867
5093
|
const containerRect = currentContainer.getBoundingClientRect();
|
|
4868
5094
|
const currentZoom = transformations.current.zoom;
|
|
4869
5095
|
const contentPosition = [
|
|
4870
|
-
|
|
4871
|
-
|
|
5096
|
+
origin[0] - elementRect.left,
|
|
5097
|
+
origin[1] - elementRect.top
|
|
4872
5098
|
];
|
|
4873
5099
|
const containerPosition = [
|
|
4874
|
-
|
|
4875
|
-
|
|
5100
|
+
origin[0] - containerRect.left,
|
|
5101
|
+
origin[1] - containerRect.top
|
|
4876
5102
|
];
|
|
4877
|
-
|
|
5103
|
+
originRef.current = [
|
|
4878
5104
|
contentPosition[0] / currentZoom,
|
|
4879
5105
|
contentPosition[1] / currentZoom
|
|
4880
|
-
]
|
|
5106
|
+
];
|
|
4881
5107
|
return {
|
|
4882
5108
|
contentPosition,
|
|
4883
5109
|
containerPosition,
|
|
@@ -4926,11 +5152,13 @@ var useViewportContainer = ({
|
|
|
4926
5152
|
}
|
|
4927
5153
|
);
|
|
4928
5154
|
return {
|
|
4929
|
-
origin
|
|
5155
|
+
origin: originRef.current
|
|
4930
5156
|
};
|
|
4931
5157
|
};
|
|
5158
|
+
var selectLargestPageWidth = (state) => state.viewports.reduce((max, vp) => Math.max(max, vp.width), 0);
|
|
4932
5159
|
var DEFAULT_HEIGHT = 600;
|
|
4933
5160
|
var EXTRA_HEIGHT = 0;
|
|
5161
|
+
var DEFAULT_VIRTUALIZER_OPTIONS = { overscan: 1 };
|
|
4934
5162
|
var VirtualizedPageItem = memo(
|
|
4935
5163
|
({ child, virtualItem, innerBoxWidth }) => {
|
|
4936
5164
|
return /* @__PURE__ */ jsx(
|
|
@@ -4963,7 +5191,7 @@ VirtualizedPageItem.displayName = "VirtualizedPageItem";
|
|
|
4963
5191
|
var Pages = ({
|
|
4964
5192
|
children,
|
|
4965
5193
|
gap = 10,
|
|
4966
|
-
virtualizerOptions =
|
|
5194
|
+
virtualizerOptions = DEFAULT_VIRTUALIZER_OPTIONS,
|
|
4967
5195
|
initialOffset,
|
|
4968
5196
|
onOffsetChange,
|
|
4969
5197
|
...props
|
|
@@ -4983,12 +5211,16 @@ var Pages = ({
|
|
|
4983
5211
|
const setVirtualizer = usePdf((state) => state.setVirtualizer);
|
|
4984
5212
|
const { scrollToFn } = useScrollFn();
|
|
4985
5213
|
const { observeElementOffset } = useObserveElement();
|
|
5214
|
+
const viewportsRef = useRef(viewports);
|
|
5215
|
+
viewportsRef.current = viewports;
|
|
4986
5216
|
const estimateSize = useCallback(
|
|
4987
5217
|
(index) => {
|
|
4988
|
-
|
|
4989
|
-
|
|
5218
|
+
const vp = viewportsRef.current;
|
|
5219
|
+
if (!vp || !vp[index]) return DEFAULT_HEIGHT;
|
|
5220
|
+
return vp[index].height + EXTRA_HEIGHT;
|
|
4990
5221
|
},
|
|
4991
|
-
[
|
|
5222
|
+
[]
|
|
5223
|
+
// Stable — reads from ref
|
|
4992
5224
|
);
|
|
4993
5225
|
const virtualizer = useVirtualizer({
|
|
4994
5226
|
count: numPages || 0,
|
|
@@ -5028,11 +5260,7 @@ var Pages = ({
|
|
|
5028
5260
|
items
|
|
5029
5261
|
});
|
|
5030
5262
|
useFitWidth({ viewportRef: containerRef });
|
|
5031
|
-
const largestPageWidth = usePdf(
|
|
5032
|
-
(state) => state.viewports.reduce((largestWidth, viewport) => {
|
|
5033
|
-
return Math.max(largestWidth, viewport.width);
|
|
5034
|
-
}, 0)
|
|
5035
|
-
);
|
|
5263
|
+
const largestPageWidth = usePdf(selectLargestPageWidth);
|
|
5036
5264
|
useEffect(() => {
|
|
5037
5265
|
virtualizer.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
|
|
5038
5266
|
const size = virtualizer.getSize();
|
|
@@ -5084,6 +5312,7 @@ var Pages = ({
|
|
|
5084
5312
|
alignItems: "center",
|
|
5085
5313
|
flexDirection: "column",
|
|
5086
5314
|
transformOrigin: "0 0",
|
|
5315
|
+
willChange: "transform",
|
|
5087
5316
|
// width: "max-content",
|
|
5088
5317
|
width: largestPageWidth,
|
|
5089
5318
|
margin: "0 auto"
|
|
@@ -5108,17 +5337,44 @@ var Pages = ({
|
|
|
5108
5337
|
}
|
|
5109
5338
|
);
|
|
5110
5339
|
};
|
|
5340
|
+
function buildDocumentInitParams(source, overrides) {
|
|
5341
|
+
let params;
|
|
5342
|
+
if (typeof source === "string" || source instanceof URL) {
|
|
5343
|
+
params = { url: source };
|
|
5344
|
+
} else if (source instanceof ArrayBuffer || ArrayBuffer.isView(source)) {
|
|
5345
|
+
params = { data: source };
|
|
5346
|
+
} else {
|
|
5347
|
+
params = { ...source };
|
|
5348
|
+
}
|
|
5349
|
+
const defaults = {
|
|
5350
|
+
// Skip the slow _guessMax binary-search auto-detection of max canvas area.
|
|
5351
|
+
// 1 GiB = 256 megapixels at 4 bytes/pixel. If the device can't allocate
|
|
5352
|
+
// this, pdfjs falls back gracefully via try/catch.
|
|
5353
|
+
canvasMaxAreaInBytes: 1024 * 1024 * 1024,
|
|
5354
|
+
// Enable GPU-backed canvases (willReadFrequently: false). PDF viewers are
|
|
5355
|
+
// render-dominated — they don't call getImageData() — so GPU compositing
|
|
5356
|
+
// is the correct path. This is the browser's default when no hint is given.
|
|
5357
|
+
enableHWA: true,
|
|
5358
|
+
// Explicitly opt-in to OffscreenCanvas in the worker for image processing.
|
|
5359
|
+
// Already the browser default, but being explicit avoids edge cases.
|
|
5360
|
+
isOffscreenCanvasSupported: true
|
|
5361
|
+
};
|
|
5362
|
+
return { ...defaults, ...params, ...overrides };
|
|
5363
|
+
}
|
|
5111
5364
|
var usePDFDocumentContext = ({
|
|
5112
5365
|
onDocumentLoad,
|
|
5113
5366
|
source,
|
|
5114
5367
|
initialRotation = 0,
|
|
5115
5368
|
isZoomFitWidth,
|
|
5116
5369
|
zoom = 1,
|
|
5117
|
-
zoomOptions
|
|
5370
|
+
zoomOptions,
|
|
5371
|
+
documentOptions
|
|
5118
5372
|
}) => {
|
|
5119
5373
|
const [_, setProgress] = useState(0);
|
|
5120
5374
|
const [initialState, setInitialState] = useState();
|
|
5121
5375
|
const [rotation] = useState(initialRotation);
|
|
5376
|
+
const documentOptionsRef = useRef(documentOptions);
|
|
5377
|
+
documentOptionsRef.current = documentOptions;
|
|
5122
5378
|
useEffect(() => {
|
|
5123
5379
|
const generateViewports = async (pdf) => {
|
|
5124
5380
|
const pageProxies = [];
|
|
@@ -5136,9 +5392,9 @@ var usePDFDocumentContext = ({
|
|
|
5136
5392
|
return viewport;
|
|
5137
5393
|
})
|
|
5138
5394
|
);
|
|
5139
|
-
const sortedPageProxies = pageProxies.
|
|
5140
|
-
|
|
5141
|
-
|
|
5395
|
+
const sortedPageProxies = pageProxies.toSorted(
|
|
5396
|
+
(a, b) => a.pageNumber - b.pageNumber
|
|
5397
|
+
);
|
|
5142
5398
|
setInitialState((prev) => ({
|
|
5143
5399
|
...prev,
|
|
5144
5400
|
isZoomFitWidth,
|
|
@@ -5158,7 +5414,9 @@ var usePDFDocumentContext = ({
|
|
|
5158
5414
|
if (isDisposed) {
|
|
5159
5415
|
return;
|
|
5160
5416
|
}
|
|
5161
|
-
loadingTask = getDocument(
|
|
5417
|
+
loadingTask = getDocument(
|
|
5418
|
+
buildDocumentInitParams(source, documentOptionsRef.current)
|
|
5419
|
+
);
|
|
5162
5420
|
loadingTask.onProgress = (progressEvent) => {
|
|
5163
5421
|
if (progressEvent.loaded === progressEvent.total) {
|
|
5164
5422
|
return;
|
|
@@ -5204,6 +5462,7 @@ var Root = forwardRef(
|
|
|
5204
5462
|
isZoomFitWidth,
|
|
5205
5463
|
zoom,
|
|
5206
5464
|
zoomOptions,
|
|
5465
|
+
documentOptions,
|
|
5207
5466
|
...props
|
|
5208
5467
|
}, ref) => {
|
|
5209
5468
|
const { initialState } = usePDFDocumentContext({
|
|
@@ -5211,12 +5470,20 @@ var Root = forwardRef(
|
|
|
5211
5470
|
onDocumentLoad,
|
|
5212
5471
|
isZoomFitWidth,
|
|
5213
5472
|
zoom,
|
|
5214
|
-
zoomOptions
|
|
5473
|
+
zoomOptions,
|
|
5474
|
+
documentOptions
|
|
5215
5475
|
});
|
|
5216
5476
|
const linkService = useCreatePDFLinkService(
|
|
5217
5477
|
initialState?.pdfDocumentProxy ?? null
|
|
5218
5478
|
);
|
|
5219
|
-
|
|
5479
|
+
const documentId = initialState?.pdfDocumentProxy.fingerprints[0];
|
|
5480
|
+
useEffect(() => {
|
|
5481
|
+
if (!documentId) return;
|
|
5482
|
+
return () => {
|
|
5483
|
+
renderCache.invalidateDocument(documentId);
|
|
5484
|
+
};
|
|
5485
|
+
}, [documentId]);
|
|
5486
|
+
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
5487
|
}
|
|
5221
5488
|
);
|
|
5222
5489
|
Root.displayName = "Root";
|
|
@@ -5266,8 +5533,8 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5266
5533
|
const isVisible = isFirstPage || debouncedVisible;
|
|
5267
5534
|
useEffect(() => {
|
|
5268
5535
|
const renderThumbnail = async () => {
|
|
5269
|
-
const
|
|
5270
|
-
if (!
|
|
5536
|
+
const canvas2 = canvasRef.current;
|
|
5537
|
+
if (!canvas2 || !pageProxy) return;
|
|
5271
5538
|
try {
|
|
5272
5539
|
if (renderTaskRef.current) {
|
|
5273
5540
|
renderTaskRef.current.cancel();
|
|
@@ -5275,12 +5542,12 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5275
5542
|
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
5276
5543
|
const scale = Math.min(maxWidth / viewport.width, maxHeight / viewport.height) * (isVisible ? dpr : 0.5);
|
|
5277
5544
|
const scaledViewport = pageProxy.getViewport({ scale });
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
const context =
|
|
5545
|
+
canvas2.width = scaledViewport.width;
|
|
5546
|
+
canvas2.height = scaledViewport.height;
|
|
5547
|
+
const context = canvas2.getContext("2d");
|
|
5281
5548
|
if (!context) return;
|
|
5282
5549
|
const renderTask = pageProxy.render({
|
|
5283
|
-
canvas,
|
|
5550
|
+
canvas: canvas2,
|
|
5284
5551
|
canvasContext: context,
|
|
5285
5552
|
viewport: scaledViewport
|
|
5286
5553
|
});
|
|
@@ -5295,8 +5562,13 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5295
5562
|
}
|
|
5296
5563
|
};
|
|
5297
5564
|
renderThumbnail();
|
|
5565
|
+
const canvas = canvasRef.current;
|
|
5298
5566
|
return () => {
|
|
5299
5567
|
renderTaskRef.current?.cancel();
|
|
5568
|
+
if (canvas) {
|
|
5569
|
+
canvas.width = 1;
|
|
5570
|
+
canvas.height = 1;
|
|
5571
|
+
}
|
|
5300
5572
|
};
|
|
5301
5573
|
}, [pageProxy, isVisible, dpr, maxHeight, maxWidth]);
|
|
5302
5574
|
return {
|
|
@@ -5375,12 +5647,12 @@ var ZoomOut = ({ ...props }) => {
|
|
|
5375
5647
|
);
|
|
5376
5648
|
};
|
|
5377
5649
|
var CurrentZoom = ({ ...props }) => {
|
|
5378
|
-
const
|
|
5650
|
+
const updateZoom = usePdf((state) => state.updateZoom);
|
|
5379
5651
|
const realZoom = usePdf((state) => state.zoom);
|
|
5380
5652
|
const [zoom, setZoom] = useState((realZoom * 100).toFixed(0));
|
|
5381
|
-
const
|
|
5653
|
+
const isInputFocused = useRef(false);
|
|
5382
5654
|
useEffect(() => {
|
|
5383
|
-
if (
|
|
5655
|
+
if (isInputFocused.current) {
|
|
5384
5656
|
return;
|
|
5385
5657
|
}
|
|
5386
5658
|
setZoom((realZoom * 100).toFixed(0));
|
|
@@ -5391,14 +5663,14 @@ var CurrentZoom = ({ ...props }) => {
|
|
|
5391
5663
|
...props,
|
|
5392
5664
|
value: zoom,
|
|
5393
5665
|
onClick: () => {
|
|
5394
|
-
|
|
5666
|
+
isInputFocused.current = true;
|
|
5395
5667
|
},
|
|
5396
5668
|
onChange: (e) => {
|
|
5397
|
-
|
|
5669
|
+
updateZoom(Number(e.target.value) / 100);
|
|
5398
5670
|
setZoom(e.target.value);
|
|
5399
5671
|
},
|
|
5400
5672
|
onBlur: () => {
|
|
5401
|
-
|
|
5673
|
+
isInputFocused.current = false;
|
|
5402
5674
|
setZoom((realZoom * 100).toFixed(0));
|
|
5403
5675
|
}
|
|
5404
5676
|
}
|
|
@@ -5631,4 +5903,7 @@ function mergeAdjacentRects(rects) {
|
|
|
5631
5903
|
return merged;
|
|
5632
5904
|
}
|
|
5633
5905
|
|
|
5634
|
-
|
|
5906
|
+
// src/hooks/usePageRendered.ts
|
|
5907
|
+
var usePageRendered = (pageNumber) => usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
5908
|
+
|
|
5909
|
+
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.2",
|
|
4
|
+
"description": "A headless PDF viewer for React",
|
|
5
5
|
"author": "andrewdr (https://github.com/andrewdoro)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|