@anaralabs/lector 3.9.1 → 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 +97 -18
- package/dist/index.d.ts +1 -0
- package/dist/index.js +851 -446
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { createContext,
|
|
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';
|
|
7
|
-
import { jsx,
|
|
8
|
-
import { AnnotationLayer, TextLayer, getDocument } from 'pdfjs-dist';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
8
|
import { useDebounce } from 'use-debounce';
|
|
10
9
|
import { v4 } from 'uuid';
|
|
11
10
|
import { Slot } from '@radix-ui/react-slot';
|
|
12
11
|
import { useVirtualizer, elementScroll, debounce } from '@tanstack/react-virtual';
|
|
13
12
|
|
|
14
|
-
// src/components/annotation-tooltip.tsx
|
|
15
|
-
|
|
16
13
|
// src/lib/clamp.ts
|
|
17
14
|
var clamp = (value, minimum, maximum) => {
|
|
18
15
|
return Math.min(Math.max(value, minimum), maximum);
|
|
@@ -127,7 +124,19 @@ var PDFStore = createZustandContext(
|
|
|
127
124
|
coloredHighlights: prevState.coloredHighlights.filter(
|
|
128
125
|
(rect) => rect.uuid !== uuid
|
|
129
126
|
)
|
|
130
|
-
}))
|
|
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
|
+
}
|
|
131
140
|
}));
|
|
132
141
|
}
|
|
133
142
|
);
|
|
@@ -465,12 +474,15 @@ var AnnotationHighlightLayer = ({
|
|
|
465
474
|
}) => {
|
|
466
475
|
const { annotations } = useAnnotations();
|
|
467
476
|
const pageNumber = usePDFPageNumber();
|
|
468
|
-
const
|
|
469
|
-
|
|
477
|
+
const isPageRendered = usePdf((state) => !!state.renderedPages[pageNumber]);
|
|
478
|
+
const pageAnnotations = useMemo(
|
|
479
|
+
() => annotations.filter((a) => a.pageNumber === pageNumber),
|
|
480
|
+
[annotations, pageNumber]
|
|
470
481
|
);
|
|
482
|
+
if (!isPageRendered) return null;
|
|
471
483
|
const getCommentIconPosition = (highlights) => {
|
|
472
484
|
if (!highlights.length) return { top: 0, right: 10 };
|
|
473
|
-
const sortedHighlights =
|
|
485
|
+
const sortedHighlights = highlights.toSorted((a, b) => {
|
|
474
486
|
const topDiff = a.top - b.top;
|
|
475
487
|
return Math.abs(topDiff) < 3 ? a.left - b.left : topDiff;
|
|
476
488
|
});
|
|
@@ -615,6 +627,56 @@ function clsx() {
|
|
|
615
627
|
}
|
|
616
628
|
var clsx_default = clsx;
|
|
617
629
|
|
|
630
|
+
// src/lib/annotation-layer-styles.ts
|
|
631
|
+
var ANNOTATION_LAYER_STYLE_ID = "lector-annotation-layer-styles";
|
|
632
|
+
var annotationLayerStyles = `
|
|
633
|
+
.annotationLayer {
|
|
634
|
+
position: absolute;
|
|
635
|
+
left: 0;
|
|
636
|
+
top: 0;
|
|
637
|
+
right: 0;
|
|
638
|
+
bottom: 0;
|
|
639
|
+
overflow: hidden;
|
|
640
|
+
opacity: 1;
|
|
641
|
+
z-index: 3;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
.annotationLayer section {
|
|
645
|
+
position: absolute;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.annotationLayer .linkAnnotation > a,
|
|
649
|
+
.annotationLayer .buttonWidgetAnnotation.pushButton > a {
|
|
650
|
+
position: absolute;
|
|
651
|
+
font-size: 1em;
|
|
652
|
+
top: 0;
|
|
653
|
+
left: 0;
|
|
654
|
+
width: 100%;
|
|
655
|
+
height: 100%;
|
|
656
|
+
background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
|
|
657
|
+
cursor: pointer;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.annotationLayer .linkAnnotation > a:hover,
|
|
661
|
+
.annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
|
|
662
|
+
opacity: 0.2;
|
|
663
|
+
background: rgba(255, 255, 0, 1);
|
|
664
|
+
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
|
665
|
+
}
|
|
666
|
+
`;
|
|
667
|
+
var ensureAnnotationLayerStyles = () => {
|
|
668
|
+
if (typeof document === "undefined") {
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (document.getElementById(ANNOTATION_LAYER_STYLE_ID)) {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
const style = document.createElement("style");
|
|
675
|
+
style.id = ANNOTATION_LAYER_STYLE_ID;
|
|
676
|
+
style.textContent = annotationLayerStyles;
|
|
677
|
+
document.head.appendChild(style);
|
|
678
|
+
};
|
|
679
|
+
|
|
618
680
|
// src/lib/cancellable.ts
|
|
619
681
|
var cancellable = (promise) => {
|
|
620
682
|
let isCancelled = false;
|
|
@@ -639,6 +701,15 @@ var cancellable = (promise) => {
|
|
|
639
701
|
}
|
|
640
702
|
};
|
|
641
703
|
};
|
|
704
|
+
|
|
705
|
+
// src/lib/pdfjs.ts
|
|
706
|
+
var pdfJsPromise = null;
|
|
707
|
+
var loadPdfJs = () => {
|
|
708
|
+
if (!pdfJsPromise) {
|
|
709
|
+
pdfJsPromise = import('pdfjs-dist/legacy/build/pdf.mjs');
|
|
710
|
+
}
|
|
711
|
+
return pdfJsPromise;
|
|
712
|
+
};
|
|
642
713
|
var usePdfJump = () => {
|
|
643
714
|
const virtualizer = usePdf((state) => state.virtualizer);
|
|
644
715
|
const setHighlight = usePdf((state) => state.setHighlight);
|
|
@@ -931,46 +1002,7 @@ var useAnnotationLayer = (params) => {
|
|
|
931
1002
|
};
|
|
932
1003
|
}, [jumpToPage, linkService, mergedParams.jumpOptions]);
|
|
933
1004
|
useEffect(() => {
|
|
934
|
-
|
|
935
|
-
style.textContent = `
|
|
936
|
-
.annotationLayer {
|
|
937
|
-
position: absolute;
|
|
938
|
-
left: 0;
|
|
939
|
-
top: 0;
|
|
940
|
-
right: 0;
|
|
941
|
-
bottom: 0;
|
|
942
|
-
overflow: hidden;
|
|
943
|
-
opacity: 1;
|
|
944
|
-
z-index: 3;
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
.annotationLayer section {
|
|
948
|
-
position: absolute;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
.annotationLayer .linkAnnotation > a,
|
|
952
|
-
.annotationLayer .buttonWidgetAnnotation.pushButton > a {
|
|
953
|
-
position: absolute;
|
|
954
|
-
font-size: 1em;
|
|
955
|
-
top: 0;
|
|
956
|
-
left: 0;
|
|
957
|
-
width: 100%;
|
|
958
|
-
height: 100%;
|
|
959
|
-
background: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7") 0 0 repeat;
|
|
960
|
-
cursor: pointer;
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
.annotationLayer .linkAnnotation > a:hover,
|
|
964
|
-
.annotationLayer .buttonWidgetAnnotation.pushButton > a:hover {
|
|
965
|
-
opacity: 0.2;
|
|
966
|
-
background: rgba(255, 255, 0, 1);
|
|
967
|
-
box-shadow: 0 2px 10px rgba(255, 255, 0, 1);
|
|
968
|
-
}
|
|
969
|
-
`;
|
|
970
|
-
document.head.appendChild(style);
|
|
971
|
-
return () => {
|
|
972
|
-
document.head.removeChild(style);
|
|
973
|
-
};
|
|
1005
|
+
ensureAnnotationLayerStyles();
|
|
974
1006
|
}, []);
|
|
975
1007
|
useEffect(() => {
|
|
976
1008
|
if (!annotationLayerRef.current) return;
|
|
@@ -1011,22 +1043,27 @@ var useAnnotationLayer = (params) => {
|
|
|
1011
1043
|
}
|
|
1012
1044
|
annotationLayerRef.current.innerHTML = "";
|
|
1013
1045
|
annotationLayerRef.current.className = "annotationLayer";
|
|
1014
|
-
const viewport = pdfPageProxy.getViewport({ scale: 1 })
|
|
1046
|
+
const viewport = pdfPageProxy.getViewport({ scale: 1 }).clone({
|
|
1047
|
+
dontFlip: true
|
|
1048
|
+
});
|
|
1015
1049
|
const annotationLayerConfig = {
|
|
1016
1050
|
div: annotationLayerRef.current,
|
|
1017
1051
|
viewport,
|
|
1018
1052
|
page: pdfPageProxy,
|
|
1019
1053
|
linkService,
|
|
1054
|
+
annotationStorage: void 0,
|
|
1020
1055
|
accessibilityManager: void 0,
|
|
1021
1056
|
annotationCanvasMap: void 0,
|
|
1022
1057
|
annotationEditorUIManager: void 0,
|
|
1023
|
-
structTreeLayer: void 0
|
|
1058
|
+
structTreeLayer: void 0,
|
|
1059
|
+
commentManager: void 0
|
|
1024
1060
|
};
|
|
1025
|
-
const annotationLayer = new AnnotationLayer(annotationLayerConfig);
|
|
1026
|
-
annotationLayerObjectRef.current = annotationLayer;
|
|
1027
1061
|
const { cancel } = cancellable(
|
|
1028
1062
|
(async () => {
|
|
1029
1063
|
try {
|
|
1064
|
+
const { AnnotationLayer: AnnotationLayer2 } = await loadPdfJs();
|
|
1065
|
+
const annotationLayer = new AnnotationLayer2(annotationLayerConfig);
|
|
1066
|
+
annotationLayerObjectRef.current = annotationLayer;
|
|
1030
1067
|
const annotations = await pdfPageProxy.getAnnotations();
|
|
1031
1068
|
await annotationLayer.render({
|
|
1032
1069
|
...annotationLayerConfig,
|
|
@@ -1046,34 +1083,177 @@ var useAnnotationLayer = (params) => {
|
|
|
1046
1083
|
annotationLayerRef
|
|
1047
1084
|
};
|
|
1048
1085
|
};
|
|
1049
|
-
var
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1086
|
+
var AnnotationLayer = memo(
|
|
1087
|
+
({
|
|
1088
|
+
renderForms = true,
|
|
1089
|
+
externalLinksEnabled = true,
|
|
1090
|
+
jumpOptions = { behavior: "smooth", align: "start" },
|
|
1091
|
+
className,
|
|
1092
|
+
style,
|
|
1093
|
+
...props
|
|
1094
|
+
}) => {
|
|
1095
|
+
const { annotationLayerRef } = useAnnotationLayer({
|
|
1096
|
+
renderForms,
|
|
1097
|
+
externalLinksEnabled,
|
|
1098
|
+
jumpOptions
|
|
1099
|
+
});
|
|
1100
|
+
return /* @__PURE__ */ jsx(
|
|
1101
|
+
"div",
|
|
1102
|
+
{
|
|
1103
|
+
className: clsx_default("annotationLayer", className),
|
|
1104
|
+
style: {
|
|
1105
|
+
...style,
|
|
1106
|
+
position: "absolute",
|
|
1107
|
+
top: 0,
|
|
1108
|
+
left: 0
|
|
1109
|
+
},
|
|
1110
|
+
...props,
|
|
1111
|
+
ref: annotationLayerRef
|
|
1112
|
+
}
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
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)
|
|
1075
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
|
+
}
|
|
1076
1255
|
};
|
|
1256
|
+
var renderQueue = new RenderQueue();
|
|
1077
1257
|
var useDpr = () => {
|
|
1078
1258
|
const [dpr, setDPR] = useState(
|
|
1079
1259
|
typeof window === "undefined" ? 1 : Math.min(window.devicePixelRatio, 2)
|
|
@@ -1097,83 +1277,206 @@ var useDpr = () => {
|
|
|
1097
1277
|
};
|
|
1098
1278
|
|
|
1099
1279
|
// src/hooks/layers/useCanvasLayer.tsx
|
|
1100
|
-
var MAX_CANVAS_PIXELS = 16777216;
|
|
1101
|
-
var MAX_CANVAS_DIMENSION = 32767;
|
|
1102
1280
|
var useCanvasLayer = ({ background }) => {
|
|
1103
1281
|
const canvasRef = useRef(null);
|
|
1104
1282
|
const pageNumber = usePDFPageNumber();
|
|
1105
1283
|
const dpr = useDpr();
|
|
1106
1284
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1107
1285
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1108
|
-
const
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
return 0;
|
|
1113
|
-
}
|
|
1114
|
-
const areaLimit = Math.sqrt(
|
|
1115
|
-
MAX_CANVAS_PIXELS / Math.max(pageWidth * pageHeight, 1)
|
|
1116
|
-
);
|
|
1117
|
-
const widthLimit = MAX_CANVAS_DIMENSION / Math.max(pageWidth, 1);
|
|
1118
|
-
const heightLimit = MAX_CANVAS_DIMENSION / Math.max(pageHeight, 1);
|
|
1119
|
-
const safeScale = Math.min(
|
|
1120
|
-
targetScale,
|
|
1121
|
-
Number.isFinite(areaLimit) ? areaLimit : targetScale,
|
|
1122
|
-
Number.isFinite(widthLimit) ? widthLimit : targetScale,
|
|
1123
|
-
Number.isFinite(heightLimit) ? heightLimit : targetScale
|
|
1124
|
-
);
|
|
1125
|
-
return Math.max(safeScale, 0);
|
|
1126
|
-
},
|
|
1127
|
-
[]
|
|
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"
|
|
1128
1290
|
);
|
|
1291
|
+
const [zoom] = useDebounce(bouncyZoom, 100);
|
|
1292
|
+
const lastRenderedScaleRef = useRef(0);
|
|
1293
|
+
const lastBackgroundRef = useRef(background);
|
|
1294
|
+
const lastPageProxyRef = useRef(pdfPageProxy);
|
|
1129
1295
|
useLayoutEffect(() => {
|
|
1130
1296
|
if (!canvasRef.current) {
|
|
1131
1297
|
return;
|
|
1132
1298
|
}
|
|
1133
|
-
|
|
1299
|
+
if (lastPageProxyRef.current !== pdfPageProxy || lastBackgroundRef.current !== background) {
|
|
1300
|
+
lastPageProxyRef.current = pdfPageProxy;
|
|
1301
|
+
lastBackgroundRef.current = background;
|
|
1302
|
+
lastRenderedScaleRef.current = 0;
|
|
1303
|
+
}
|
|
1134
1304
|
const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
1135
1305
|
const pageWidth = baseViewport.width;
|
|
1136
1306
|
const pageHeight = baseViewport.height;
|
|
1307
|
+
const bgColor = background ?? "white";
|
|
1137
1308
|
const targetBaseScale = dpr * Math.min(zoom, 1);
|
|
1138
1309
|
const baseScale = clampScaleForPage(targetBaseScale, pageWidth, pageHeight);
|
|
1139
|
-
|
|
1140
|
-
baseCanvas.height = Math.floor(pageHeight * baseScale);
|
|
1141
|
-
baseCanvas.style.position = "absolute";
|
|
1142
|
-
baseCanvas.style.top = "0";
|
|
1143
|
-
baseCanvas.style.left = "0";
|
|
1144
|
-
baseCanvas.style.width = `${pageWidth}px`;
|
|
1145
|
-
baseCanvas.style.height = `${pageHeight}px`;
|
|
1146
|
-
baseCanvas.style.transform = "translate(0px, 0px)";
|
|
1147
|
-
baseCanvas.style.zIndex = "0";
|
|
1148
|
-
baseCanvas.style.pointerEvents = "none";
|
|
1149
|
-
const context = baseCanvas.getContext("2d");
|
|
1150
|
-
if (!context) {
|
|
1310
|
+
if (baseScale === lastRenderedScaleRef.current) {
|
|
1151
1311
|
return;
|
|
1152
1312
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
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);
|
|
1164
1325
|
}
|
|
1165
|
-
|
|
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
|
+
});
|
|
1166
1385
|
});
|
|
1167
1386
|
return () => {
|
|
1168
|
-
|
|
1387
|
+
cancelled = true;
|
|
1388
|
+
job.cancel();
|
|
1389
|
+
void activeRenderingTask?.cancel();
|
|
1169
1390
|
};
|
|
1170
|
-
}, [
|
|
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]);
|
|
1171
1410
|
return {
|
|
1172
1411
|
canvasRef
|
|
1173
1412
|
};
|
|
1174
1413
|
};
|
|
1175
|
-
|
|
1176
|
-
|
|
1414
|
+
|
|
1415
|
+
// src/lib/viewport-invalidation.ts
|
|
1416
|
+
var viewportInvalidationRegistry = /* @__PURE__ */ new WeakMap();
|
|
1417
|
+
var notifyCallbacks = (entry) => {
|
|
1418
|
+
entry.rafId = null;
|
|
1419
|
+
entry.callbacks.forEach((callback) => {
|
|
1420
|
+
callback();
|
|
1421
|
+
});
|
|
1422
|
+
};
|
|
1423
|
+
var scheduleInvalidation = (entry) => {
|
|
1424
|
+
if (entry.rafId !== null) {
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
entry.rafId = requestAnimationFrame(() => {
|
|
1428
|
+
notifyCallbacks(entry);
|
|
1429
|
+
});
|
|
1430
|
+
};
|
|
1431
|
+
var subscribeToViewportInvalidation = (viewport, callback) => {
|
|
1432
|
+
let entry = viewportInvalidationRegistry.get(viewport);
|
|
1433
|
+
if (!entry) {
|
|
1434
|
+
const callbacks = /* @__PURE__ */ new Set();
|
|
1435
|
+
const schedule = () => {
|
|
1436
|
+
const currentEntry = viewportInvalidationRegistry.get(viewport);
|
|
1437
|
+
if (!currentEntry) {
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
scheduleInvalidation(currentEntry);
|
|
1441
|
+
};
|
|
1442
|
+
entry = {
|
|
1443
|
+
callbacks,
|
|
1444
|
+
scrollListener: schedule,
|
|
1445
|
+
windowResizeListener: schedule,
|
|
1446
|
+
resizeObserver: typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule),
|
|
1447
|
+
rafId: null
|
|
1448
|
+
};
|
|
1449
|
+
viewport.addEventListener("scroll", entry.scrollListener, {
|
|
1450
|
+
passive: true
|
|
1451
|
+
});
|
|
1452
|
+
window.addEventListener("resize", entry.windowResizeListener, {
|
|
1453
|
+
passive: true
|
|
1454
|
+
});
|
|
1455
|
+
entry.resizeObserver?.observe(viewport);
|
|
1456
|
+
viewportInvalidationRegistry.set(viewport, entry);
|
|
1457
|
+
}
|
|
1458
|
+
entry.callbacks.add(callback);
|
|
1459
|
+
return () => {
|
|
1460
|
+
const currentEntry = viewportInvalidationRegistry.get(viewport);
|
|
1461
|
+
if (!currentEntry) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
currentEntry.callbacks.delete(callback);
|
|
1465
|
+
if (currentEntry.callbacks.size > 0) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (currentEntry.rafId !== null) {
|
|
1469
|
+
cancelAnimationFrame(currentEntry.rafId);
|
|
1470
|
+
}
|
|
1471
|
+
viewport.removeEventListener("scroll", currentEntry.scrollListener);
|
|
1472
|
+
window.removeEventListener("resize", currentEntry.windowResizeListener);
|
|
1473
|
+
currentEntry.resizeObserver?.disconnect();
|
|
1474
|
+
viewportInvalidationRegistry.delete(viewport);
|
|
1475
|
+
};
|
|
1476
|
+
};
|
|
1477
|
+
|
|
1478
|
+
// src/hooks/layers/useDetailCanvasLayer.tsx
|
|
1479
|
+
var DETAIL_RENDER_IDLE_MS = 80;
|
|
1177
1480
|
var useDetailCanvasLayer = ({
|
|
1178
1481
|
background,
|
|
1179
1482
|
baseCanvasRef
|
|
@@ -1183,196 +1486,200 @@ var useDetailCanvasLayer = ({
|
|
|
1183
1486
|
const pageNumber = usePDFPageNumber();
|
|
1184
1487
|
const dpr = useDpr();
|
|
1185
1488
|
const bouncyZoom = usePdf((state) => state.zoom);
|
|
1489
|
+
const isPinching = usePdf((state) => state.isPinching);
|
|
1186
1490
|
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
1187
1491
|
const viewportRef = usePdf((state) => state.viewportRef);
|
|
1188
1492
|
const [zoom] = useDebounce(bouncyZoom, 200);
|
|
1189
|
-
const [scrollTick, setScrollTick] = useState(0);
|
|
1190
|
-
const [debouncedScrollTick] = useDebounce(scrollTick, 20);
|
|
1191
|
-
const ensureDetailCanvas = useCallback(() => {
|
|
1192
|
-
let detailCanvas = detailCanvasRef.current;
|
|
1193
|
-
if (!detailCanvas) {
|
|
1194
|
-
const parent = baseCanvasRef.current?.parentElement;
|
|
1195
|
-
if (!parent) {
|
|
1196
|
-
return null;
|
|
1197
|
-
}
|
|
1198
|
-
detailCanvas = document.createElement("canvas");
|
|
1199
|
-
parent.appendChild(detailCanvas);
|
|
1200
|
-
detailCanvasRef.current = detailCanvas;
|
|
1201
|
-
}
|
|
1202
|
-
detailCanvas.style.position = "absolute";
|
|
1203
|
-
detailCanvas.style.top = "0";
|
|
1204
|
-
detailCanvas.style.left = "0";
|
|
1205
|
-
detailCanvas.style.pointerEvents = "none";
|
|
1206
|
-
detailCanvas.style.zIndex = "0";
|
|
1207
|
-
return detailCanvas;
|
|
1208
|
-
}, [baseCanvasRef]);
|
|
1209
|
-
const clampScaleForPage = useCallback(
|
|
1210
|
-
(targetScale, pageWidth, pageHeight) => {
|
|
1211
|
-
if (!targetScale) {
|
|
1212
|
-
return 0;
|
|
1213
|
-
}
|
|
1214
|
-
const areaLimit = Math.sqrt(
|
|
1215
|
-
MAX_CANVAS_PIXELS2 / Math.max(pageWidth * pageHeight, 1)
|
|
1216
|
-
);
|
|
1217
|
-
const widthLimit = MAX_CANVAS_DIMENSION2 / Math.max(pageWidth, 1);
|
|
1218
|
-
const heightLimit = MAX_CANVAS_DIMENSION2 / Math.max(pageHeight, 1);
|
|
1219
|
-
const safeScale = Math.min(
|
|
1220
|
-
targetScale,
|
|
1221
|
-
Number.isFinite(areaLimit) ? areaLimit : targetScale,
|
|
1222
|
-
Number.isFinite(widthLimit) ? widthLimit : targetScale,
|
|
1223
|
-
Number.isFinite(heightLimit) ? heightLimit : targetScale
|
|
1224
|
-
);
|
|
1225
|
-
return Math.max(safeScale, 0);
|
|
1226
|
-
},
|
|
1227
|
-
[]
|
|
1228
|
-
);
|
|
1229
1493
|
useLayoutEffect(() => {
|
|
1230
1494
|
const scrollContainer = viewportRef?.current;
|
|
1231
|
-
if (!scrollContainer)
|
|
1232
|
-
const handleScroll = () => {
|
|
1233
|
-
setScrollTick((prev) => prev + 1);
|
|
1234
|
-
};
|
|
1235
|
-
scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
|
|
1236
|
-
return () => {
|
|
1237
|
-
scrollContainer.removeEventListener("scroll", handleScroll);
|
|
1238
|
-
};
|
|
1239
|
-
}, [viewportRef?.current]);
|
|
1240
|
-
useLayoutEffect(() => {
|
|
1241
|
-
if (!viewportRef?.current) {
|
|
1495
|
+
if (!scrollContainer) {
|
|
1242
1496
|
return;
|
|
1243
1497
|
}
|
|
1244
|
-
const detailCanvas =
|
|
1498
|
+
const detailCanvas = detailCanvasRef.current;
|
|
1245
1499
|
const container = containerRef.current;
|
|
1246
1500
|
if (!detailCanvas || !container) {
|
|
1247
1501
|
return;
|
|
1248
1502
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
const viewportWidth = scrollContainer.clientWidth / zoom;
|
|
1263
|
-
const viewportHeight = scrollContainer.clientHeight / zoom;
|
|
1264
|
-
const pageRect = pageContainer.getBoundingClientRect();
|
|
1265
|
-
const containerRect = scrollContainer.getBoundingClientRect();
|
|
1266
|
-
const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
|
|
1267
|
-
const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
|
|
1268
|
-
const visibleLeft = Math.max(0, scrollX - pageLeft);
|
|
1269
|
-
const visibleTop = Math.max(0, scrollY - pageTop);
|
|
1270
|
-
const visibleRight = Math.min(
|
|
1271
|
-
pageWidth,
|
|
1272
|
-
scrollX + viewportWidth - pageLeft
|
|
1273
|
-
);
|
|
1274
|
-
const visibleBottom = Math.min(
|
|
1275
|
-
pageHeight,
|
|
1276
|
-
scrollY + viewportHeight - pageTop
|
|
1277
|
-
);
|
|
1278
|
-
const visibleWidth = Math.max(0, visibleRight - visibleLeft);
|
|
1279
|
-
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
1280
|
-
const targetDetailScale = dpr * zoom * 1.3;
|
|
1281
|
-
const baseTargetScale = dpr * Math.min(zoom, 1);
|
|
1282
|
-
const baseScale = clampScaleForPage(baseTargetScale, pageWidth, pageHeight);
|
|
1283
|
-
const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
|
|
1284
|
-
if (!needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
|
|
1285
|
-
detailCanvas.style.display = "none";
|
|
1286
|
-
detailCanvas.width = 0;
|
|
1287
|
-
detailCanvas.height = 0;
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
detailCanvas.style.display = "block";
|
|
1291
|
-
const pdfOffsetX = visibleLeft;
|
|
1292
|
-
const pdfOffsetY = visibleTop;
|
|
1293
|
-
const pdfWidth = visibleWidth * targetDetailScale;
|
|
1294
|
-
const pdfHeight = visibleHeight * targetDetailScale;
|
|
1295
|
-
const effectiveScale = targetDetailScale;
|
|
1296
|
-
const actualWidth = pdfWidth;
|
|
1297
|
-
const actualHeight = pdfHeight;
|
|
1298
|
-
detailCanvas.width = actualWidth;
|
|
1299
|
-
detailCanvas.height = actualHeight;
|
|
1300
|
-
const scaledWidth = visibleWidth * zoom;
|
|
1301
|
-
const scaledHeight = visibleHeight * zoom;
|
|
1302
|
-
detailCanvas.style.width = `${scaledWidth}px`;
|
|
1303
|
-
detailCanvas.style.height = `${scaledHeight}px`;
|
|
1304
|
-
detailCanvas.style.transformOrigin = "center center";
|
|
1305
|
-
detailCanvas.style.transform = `translate(${visibleLeft * zoom}px, ${visibleTop * zoom}px) `;
|
|
1306
|
-
container.style.transform = `scale3d(${1 / zoom}, ${1 / zoom}, 1)`;
|
|
1307
|
-
container.style.transformOrigin = `0 0`;
|
|
1308
|
-
const context = detailCanvas.getContext("2d");
|
|
1309
|
-
if (!context) {
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
context.setTransform(1, 0, 0, 1, 0, 0);
|
|
1313
|
-
context.clearRect(0, 0, detailCanvas.width, detailCanvas.height);
|
|
1314
|
-
const transform = [
|
|
1315
|
-
1,
|
|
1316
|
-
0,
|
|
1317
|
-
0,
|
|
1318
|
-
1,
|
|
1319
|
-
-pdfOffsetX * effectiveScale,
|
|
1320
|
-
-pdfOffsetY * effectiveScale
|
|
1321
|
-
];
|
|
1322
|
-
const detailViewport = pdfPageProxy.getViewport({ scale: effectiveScale });
|
|
1323
|
-
const renderingTask = pdfPageProxy.render({
|
|
1324
|
-
canvasContext: context,
|
|
1325
|
-
viewport: detailViewport,
|
|
1326
|
-
background,
|
|
1327
|
-
transform
|
|
1328
|
-
});
|
|
1329
|
-
renderingTask.promise.catch((error) => {
|
|
1330
|
-
if (error.name === "RenderingCancelledException") {
|
|
1503
|
+
let renderingTask = null;
|
|
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}`;
|
|
1507
|
+
const hideDetailCanvas = () => {
|
|
1508
|
+
renderingTask?.cancel();
|
|
1509
|
+
detailCanvas.style.cssText = `${detailBaseStyle};display:block;opacity:0`;
|
|
1510
|
+
container.style.cssText = "";
|
|
1511
|
+
};
|
|
1512
|
+
const renderDetailCanvas = () => {
|
|
1513
|
+
const pageContainer = baseCanvasRef.current?.parentElement ?? null;
|
|
1514
|
+
if (!pageContainer) {
|
|
1515
|
+
hideDetailCanvas();
|
|
1331
1516
|
return;
|
|
1332
1517
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1518
|
+
const baseViewport = pdfPageProxy.getViewport({ scale: 1 });
|
|
1519
|
+
const pageWidth = baseViewport.width;
|
|
1520
|
+
const pageHeight = baseViewport.height;
|
|
1521
|
+
const scrollX = scrollContainer.scrollLeft / zoom;
|
|
1522
|
+
const scrollY = scrollContainer.scrollTop / zoom;
|
|
1523
|
+
const viewportWidth = scrollContainer.clientWidth / zoom;
|
|
1524
|
+
const viewportHeight = scrollContainer.clientHeight / zoom;
|
|
1525
|
+
const pageRect = pageContainer.getBoundingClientRect();
|
|
1526
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
1527
|
+
const pageLeft = (pageRect.left - containerRect.left) / zoom + scrollX;
|
|
1528
|
+
const pageTop = (pageRect.top - containerRect.top) / zoom + scrollY;
|
|
1529
|
+
const visibleLeft = Math.max(0, scrollX - pageLeft);
|
|
1530
|
+
const visibleTop = Math.max(0, scrollY - pageTop);
|
|
1531
|
+
const visibleRight = Math.min(
|
|
1532
|
+
pageWidth,
|
|
1533
|
+
scrollX + viewportWidth - pageLeft
|
|
1534
|
+
);
|
|
1535
|
+
const visibleBottom = Math.min(
|
|
1536
|
+
pageHeight,
|
|
1537
|
+
scrollY + viewportHeight - pageTop
|
|
1538
|
+
);
|
|
1539
|
+
const visibleWidth = Math.max(0, visibleRight - visibleLeft);
|
|
1540
|
+
const visibleHeight = Math.max(0, visibleBottom - visibleTop);
|
|
1541
|
+
const targetDetailScale = dpr * zoom * 1.3;
|
|
1542
|
+
const baseTargetScale = dpr * Math.min(zoom, 1);
|
|
1543
|
+
const baseScale = clampScaleForPage(
|
|
1544
|
+
baseTargetScale,
|
|
1545
|
+
pageWidth,
|
|
1546
|
+
pageHeight
|
|
1547
|
+
);
|
|
1548
|
+
const needsDetail = zoom > 1 && targetDetailScale - baseScale > 1e-3;
|
|
1549
|
+
if (isPinching || !needsDetail || visibleWidth <= 0 || visibleHeight <= 0) {
|
|
1550
|
+
hideDetailCanvas();
|
|
1551
|
+
return;
|
|
1552
|
+
}
|
|
1553
|
+
renderingTask?.cancel();
|
|
1554
|
+
const pdfOffsetX = visibleLeft;
|
|
1555
|
+
const pdfOffsetY = visibleTop;
|
|
1556
|
+
const effectiveScale = targetDetailScale;
|
|
1557
|
+
const actualWidth = visibleWidth * effectiveScale;
|
|
1558
|
+
const actualHeight = visibleHeight * effectiveScale;
|
|
1559
|
+
const buffer = document.createElement("canvas");
|
|
1560
|
+
buffer.width = actualWidth;
|
|
1561
|
+
buffer.height = actualHeight;
|
|
1562
|
+
const bufferCtx = buffer.getContext("2d");
|
|
1563
|
+
if (!bufferCtx) {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
bufferCtx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1567
|
+
const transform = [
|
|
1568
|
+
1,
|
|
1569
|
+
0,
|
|
1570
|
+
0,
|
|
1571
|
+
1,
|
|
1572
|
+
-pdfOffsetX * effectiveScale,
|
|
1573
|
+
-pdfOffsetY * effectiveScale
|
|
1574
|
+
];
|
|
1575
|
+
const detailViewport = pdfPageProxy.getViewport({
|
|
1576
|
+
scale: effectiveScale
|
|
1577
|
+
});
|
|
1578
|
+
const currentRenderingTask = pdfPageProxy.render({
|
|
1579
|
+
canvas: buffer,
|
|
1580
|
+
canvasContext: bufferCtx,
|
|
1581
|
+
viewport: detailViewport,
|
|
1582
|
+
background,
|
|
1583
|
+
transform
|
|
1584
|
+
});
|
|
1585
|
+
renderingTask = currentRenderingTask;
|
|
1586
|
+
currentRenderingTask.promise.then(() => {
|
|
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);
|
|
1595
|
+
}
|
|
1596
|
+
}).catch((error) => {
|
|
1597
|
+
if (error.name === "RenderingCancelledException") {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
throw error;
|
|
1601
|
+
}).finally(() => {
|
|
1602
|
+
if (renderingTask === currentRenderingTask) {
|
|
1603
|
+
renderingTask = null;
|
|
1604
|
+
}
|
|
1605
|
+
buffer.width = 0;
|
|
1606
|
+
buffer.height = 0;
|
|
1607
|
+
});
|
|
1608
|
+
};
|
|
1609
|
+
const scheduleRender = (delay = DETAIL_RENDER_IDLE_MS) => {
|
|
1610
|
+
if (renderTimeoutId !== null) {
|
|
1611
|
+
clearTimeout(renderTimeoutId);
|
|
1612
|
+
}
|
|
1613
|
+
renderingTask?.cancel();
|
|
1614
|
+
renderTimeoutId = setTimeout(() => {
|
|
1615
|
+
renderTimeoutId = null;
|
|
1616
|
+
renderDetailCanvas();
|
|
1617
|
+
}, delay);
|
|
1618
|
+
};
|
|
1619
|
+
const unsubscribe = subscribeToViewportInvalidation(
|
|
1620
|
+
scrollContainer,
|
|
1621
|
+
scheduleRender
|
|
1622
|
+
);
|
|
1623
|
+
if (zoom <= 1 || isPinching) {
|
|
1624
|
+
hideDetailCanvas();
|
|
1625
|
+
} else {
|
|
1626
|
+
scheduleRender(0);
|
|
1627
|
+
}
|
|
1335
1628
|
return () => {
|
|
1336
|
-
|
|
1629
|
+
unsubscribe();
|
|
1630
|
+
if (renderTimeoutId !== null) {
|
|
1631
|
+
clearTimeout(renderTimeoutId);
|
|
1632
|
+
}
|
|
1633
|
+
void renderingTask?.cancel();
|
|
1337
1634
|
};
|
|
1338
1635
|
}, [
|
|
1339
1636
|
pdfPageProxy,
|
|
1340
1637
|
zoom,
|
|
1638
|
+
isPinching,
|
|
1341
1639
|
background,
|
|
1342
1640
|
dpr,
|
|
1343
1641
|
viewportRef,
|
|
1344
|
-
|
|
1345
|
-
clampScaleForPage,
|
|
1346
|
-
baseCanvasRef,
|
|
1347
|
-
debouncedScrollTick
|
|
1642
|
+
baseCanvasRef
|
|
1348
1643
|
]);
|
|
1644
|
+
useEffect(() => {
|
|
1645
|
+
const canvas = detailCanvasRef.current;
|
|
1646
|
+
return () => {
|
|
1647
|
+
if (canvas) {
|
|
1648
|
+
canvas.width = 1;
|
|
1649
|
+
canvas.height = 1;
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}, []);
|
|
1349
1653
|
return {
|
|
1350
1654
|
detailCanvasRef,
|
|
1351
1655
|
containerRef
|
|
1352
1656
|
};
|
|
1353
1657
|
};
|
|
1354
|
-
var CanvasLayer = (
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
...props
|
|
1358
|
-
}) => {
|
|
1359
|
-
const { canvasRef } = useCanvasLayer({ background });
|
|
1360
|
-
const { detailCanvasRef, containerRef } = useDetailCanvasLayer({
|
|
1658
|
+
var CanvasLayer = memo(
|
|
1659
|
+
({
|
|
1660
|
+
style,
|
|
1361
1661
|
background,
|
|
1362
|
-
|
|
1363
|
-
})
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1662
|
+
...props
|
|
1663
|
+
}) => {
|
|
1664
|
+
const { canvasRef } = useCanvasLayer({ background });
|
|
1665
|
+
const { detailCanvasRef, containerRef } = useDetailCanvasLayer({
|
|
1666
|
+
background,
|
|
1667
|
+
baseCanvasRef: canvasRef
|
|
1668
|
+
});
|
|
1669
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1670
|
+
/* @__PURE__ */ jsx("canvas", { ...props, ref: canvasRef, style }),
|
|
1671
|
+
/* @__PURE__ */ jsx(
|
|
1672
|
+
"div",
|
|
1673
|
+
{
|
|
1674
|
+
ref: containerRef,
|
|
1675
|
+
className: "absolute top-0 left-0 w-full h-full flex items-center justify-center",
|
|
1676
|
+
children: /* @__PURE__ */ jsx("canvas", { ref: detailCanvasRef })
|
|
1677
|
+
}
|
|
1678
|
+
)
|
|
1679
|
+
] });
|
|
1680
|
+
}
|
|
1681
|
+
);
|
|
1682
|
+
CanvasLayer.displayName = "CanvasLayer";
|
|
1376
1683
|
|
|
1377
1684
|
// src/hooks/useSelectionDimensions.tsx
|
|
1378
1685
|
var MERGE_THRESHOLD = 2;
|
|
@@ -2082,6 +2389,10 @@ var ColoredHighlightLayer = ({
|
|
|
2082
2389
|
(state) => state.coloredHighlights
|
|
2083
2390
|
);
|
|
2084
2391
|
const addColoredHighlight = usePdf((state) => state.addColoredHighlight);
|
|
2392
|
+
const pageHighlights = useMemo(
|
|
2393
|
+
() => highlights.filter((s) => s.pageNumber === pageNumber),
|
|
2394
|
+
[highlights, pageNumber]
|
|
2395
|
+
);
|
|
2085
2396
|
const handleHighlighting = useCallback(
|
|
2086
2397
|
(color) => {
|
|
2087
2398
|
const dimension = getDimension();
|
|
@@ -2103,13 +2414,7 @@ var ColoredHighlightLayer = ({
|
|
|
2103
2414
|
[onHighlight, getDimension, addColoredHighlight]
|
|
2104
2415
|
);
|
|
2105
2416
|
return /* @__PURE__ */ jsxs("div", { className: "colored-highlights-layer", children: [
|
|
2106
|
-
|
|
2107
|
-
ColoredHighlightComponent,
|
|
2108
|
-
{
|
|
2109
|
-
selection
|
|
2110
|
-
},
|
|
2111
|
-
selection.uuid
|
|
2112
|
-
)),
|
|
2417
|
+
pageHighlights.map((selection) => /* @__PURE__ */ jsx(ColoredHighlightComponent, { selection }, selection.uuid)),
|
|
2113
2418
|
/* @__PURE__ */ jsx(
|
|
2114
2419
|
ColorSelectionTool,
|
|
2115
2420
|
{
|
|
@@ -2118,14 +2423,13 @@ var ColoredHighlightLayer = ({
|
|
|
2118
2423
|
)
|
|
2119
2424
|
] });
|
|
2120
2425
|
};
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
};
|
|
2426
|
+
var CustomLayer = memo(
|
|
2427
|
+
({ children }) => {
|
|
2428
|
+
const pageNumber = usePDFPageNumber();
|
|
2429
|
+
return children(pageNumber);
|
|
2430
|
+
}
|
|
2431
|
+
);
|
|
2432
|
+
CustomLayer.displayName = "CustomLayer";
|
|
2129
2433
|
var convertToPercentString = (rect) => {
|
|
2130
2434
|
return {
|
|
2131
2435
|
top: `${rect.top}%`,
|
|
@@ -2138,7 +2442,10 @@ var HighlightLayer = forwardRef(({ asChild, className, style, ...props }, ref) =
|
|
|
2138
2442
|
const pageNumber = usePDFPageNumber();
|
|
2139
2443
|
const highlights = usePdf((state) => state.highlights);
|
|
2140
2444
|
const Comp = asChild ? Slot : "div";
|
|
2141
|
-
const rects =
|
|
2445
|
+
const rects = useMemo(
|
|
2446
|
+
() => highlights.filter((area) => area.pageNumber === pageNumber),
|
|
2447
|
+
[highlights, pageNumber]
|
|
2448
|
+
);
|
|
2142
2449
|
if (!rects?.length) return null;
|
|
2143
2450
|
return /* @__PURE__ */ jsx(Fragment, { children: rects.map((rect, index) => {
|
|
2144
2451
|
const { pageNumber: pageNumber2, type, style: customStyle, ...coordinates } = rect;
|
|
@@ -2319,25 +2626,33 @@ var useTextLayer = () => {
|
|
|
2319
2626
|
if (!textContainer || isRenderingRef.current) {
|
|
2320
2627
|
return;
|
|
2321
2628
|
}
|
|
2629
|
+
let isCancelled = false;
|
|
2322
2630
|
isRenderingRef.current = true;
|
|
2323
2631
|
textContainer.innerHTML = "";
|
|
2324
2632
|
if (textLayerRef.current) {
|
|
2325
2633
|
textLayerRef.current.cancel();
|
|
2326
2634
|
textLayerRef.current = null;
|
|
2327
2635
|
}
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
viewport: pdfPageProxy.getViewport({ scale: 1 })
|
|
2332
|
-
});
|
|
2333
|
-
textLayerRef.current = textLayer;
|
|
2334
|
-
textLayer.render().then(() => {
|
|
2335
|
-
if (textLayerRef.current === textLayer && textContainer) {
|
|
2336
|
-
const endOfContent = document.createElement("div");
|
|
2337
|
-
endOfContent.className = "endOfContent";
|
|
2338
|
-
textContainer.appendChild(endOfContent);
|
|
2339
|
-
bindMouseEvents(textContainer, endOfContent);
|
|
2636
|
+
void loadPdfJs().then(({ TextLayer: TextLayer2 }) => {
|
|
2637
|
+
if (isCancelled || textContainerRef.current !== textContainer) {
|
|
2638
|
+
return;
|
|
2340
2639
|
}
|
|
2640
|
+
const textLayer = new TextLayer2({
|
|
2641
|
+
textContentSource: pdfPageProxy.streamTextContent(),
|
|
2642
|
+
container: textContainer,
|
|
2643
|
+
viewport: pdfPageProxy.getViewport({ scale: 1 })
|
|
2644
|
+
});
|
|
2645
|
+
textLayerRef.current = textLayer;
|
|
2646
|
+
return textLayer.render();
|
|
2647
|
+
}).then(() => {
|
|
2648
|
+
const textLayer = textLayerRef.current;
|
|
2649
|
+
if (!textLayer || isCancelled || textContainerRef.current !== textContainer) {
|
|
2650
|
+
return;
|
|
2651
|
+
}
|
|
2652
|
+
const endOfContent = document.createElement("div");
|
|
2653
|
+
endOfContent.className = "endOfContent";
|
|
2654
|
+
textContainer.appendChild(endOfContent);
|
|
2655
|
+
bindMouseEvents(textContainer, endOfContent);
|
|
2341
2656
|
}).catch((error) => {
|
|
2342
2657
|
if (error.name !== "AbortException") {
|
|
2343
2658
|
console.error("TextLayer rendering error:", error);
|
|
@@ -2346,6 +2661,7 @@ var useTextLayer = () => {
|
|
|
2346
2661
|
isRenderingRef.current = false;
|
|
2347
2662
|
});
|
|
2348
2663
|
return () => {
|
|
2664
|
+
isCancelled = true;
|
|
2349
2665
|
isRenderingRef.current = false;
|
|
2350
2666
|
if (textLayerRef.current) {
|
|
2351
2667
|
textLayerRef.current.cancel();
|
|
@@ -2362,30 +2678,29 @@ var useTextLayer = () => {
|
|
|
2362
2678
|
pageNumber: pdfPageProxy.pageNumber
|
|
2363
2679
|
};
|
|
2364
2680
|
};
|
|
2365
|
-
var
|
|
2366
|
-
className,
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
};
|
|
2681
|
+
var TextLayer = memo(
|
|
2682
|
+
({ className, style, ...props }) => {
|
|
2683
|
+
const { textContainerRef, pageNumber } = useTextLayer();
|
|
2684
|
+
return /* @__PURE__ */ jsx(
|
|
2685
|
+
"div",
|
|
2686
|
+
{
|
|
2687
|
+
className: clsx_default("textLayer", className),
|
|
2688
|
+
style: {
|
|
2689
|
+
...style,
|
|
2690
|
+
position: "absolute",
|
|
2691
|
+
top: 0,
|
|
2692
|
+
left: 0
|
|
2693
|
+
},
|
|
2694
|
+
...props,
|
|
2695
|
+
...{
|
|
2696
|
+
"data-page-number": pageNumber
|
|
2697
|
+
},
|
|
2698
|
+
ref: textContainerRef
|
|
2699
|
+
}
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
);
|
|
2703
|
+
TextLayer.displayName = "TextLayer";
|
|
2389
2704
|
var usePDFOutline = () => {
|
|
2390
2705
|
const pdfDocumentProxy = usePdf((state) => state.pdfDocumentProxy);
|
|
2391
2706
|
const [outline, setOutline] = useState();
|
|
@@ -2523,40 +2838,44 @@ var Outline = ({
|
|
|
2523
2838
|
});
|
|
2524
2839
|
}) });
|
|
2525
2840
|
};
|
|
2526
|
-
var Page = (
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
}
|
|
2841
|
+
var Page = memo(
|
|
2842
|
+
({
|
|
2843
|
+
children,
|
|
2844
|
+
pageNumber = 1,
|
|
2845
|
+
style,
|
|
2846
|
+
...props
|
|
2847
|
+
}) => {
|
|
2848
|
+
const pdfPageProxy = usePdf((state) => state.getPdfPageProxy(pageNumber));
|
|
2849
|
+
const viewport = usePdf((state) => state.viewports[pageNumber - 1]);
|
|
2850
|
+
const width = viewport?.width ?? (pdfPageProxy.view[2] ?? 0) - (pdfPageProxy.view[0] ?? 0);
|
|
2851
|
+
const height = viewport?.height ?? (pdfPageProxy.view[3] ?? 0) - (pdfPageProxy.view[1] ?? 0);
|
|
2852
|
+
return /* @__PURE__ */ jsx(PDFPageNumberContext.Provider, { value: pdfPageProxy.pageNumber, children: /* @__PURE__ */ jsx(
|
|
2853
|
+
Primitive.div,
|
|
2854
|
+
{
|
|
2855
|
+
style: {
|
|
2856
|
+
display: "block"
|
|
2857
|
+
},
|
|
2858
|
+
children: /* @__PURE__ */ jsx(
|
|
2859
|
+
"div",
|
|
2860
|
+
{
|
|
2861
|
+
style: {
|
|
2862
|
+
"--scale-factor": 1,
|
|
2863
|
+
"--total-scale-factor": 1,
|
|
2864
|
+
backgroundColor: "white",
|
|
2865
|
+
position: "relative",
|
|
2866
|
+
width,
|
|
2867
|
+
height,
|
|
2868
|
+
...style
|
|
2869
|
+
},
|
|
2870
|
+
...props,
|
|
2871
|
+
children
|
|
2872
|
+
}
|
|
2873
|
+
)
|
|
2874
|
+
}
|
|
2875
|
+
) });
|
|
2876
|
+
}
|
|
2877
|
+
);
|
|
2878
|
+
Page.displayName = "Page";
|
|
2560
2879
|
var NextPage = () => {
|
|
2561
2880
|
};
|
|
2562
2881
|
var PreviousPage = () => {
|
|
@@ -2701,6 +3020,7 @@ var useVisiblePage = ({ items }) => {
|
|
|
2701
3020
|
const isPinching = usePdf((state) => state.isPinching);
|
|
2702
3021
|
const setCurrentPage = usePdf((state) => state.setCurrentPage);
|
|
2703
3022
|
const scrollElement = usePdf((state) => state.viewportRef?.current);
|
|
3023
|
+
const lastPageRef = useRef(0);
|
|
2704
3024
|
const calculateVisiblePageIndex = useCallback(
|
|
2705
3025
|
(virtualItems) => {
|
|
2706
3026
|
if (!scrollElement || virtualItems.length === 0) return 0;
|
|
@@ -2724,7 +3044,11 @@ var useVisiblePage = ({ items }) => {
|
|
|
2724
3044
|
useEffect(() => {
|
|
2725
3045
|
if (!isPinching && items.length > 0) {
|
|
2726
3046
|
const mostVisibleIndex = calculateVisiblePageIndex(items);
|
|
2727
|
-
|
|
3047
|
+
const page = mostVisibleIndex + 1;
|
|
3048
|
+
if (page !== lastPageRef.current) {
|
|
3049
|
+
lastPageRef.current = page;
|
|
3050
|
+
setCurrentPage?.(page);
|
|
3051
|
+
}
|
|
2728
3052
|
}
|
|
2729
3053
|
}, [items, isPinching, calculateVisiblePageIndex, setCurrentPage]);
|
|
2730
3054
|
return null;
|
|
@@ -3177,10 +3501,10 @@ var Engine = class {
|
|
|
3177
3501
|
const config = this.config;
|
|
3178
3502
|
if (!state._active) this.clean();
|
|
3179
3503
|
if ((state._blocked || !state.intentional) && !state._force && !config.triggerAllEvents) return;
|
|
3180
|
-
const
|
|
3504
|
+
const memo7 = this.handler(_objectSpread2(_objectSpread2(_objectSpread2({}, shared), state), {}, {
|
|
3181
3505
|
[this.aliasKey]: state.values
|
|
3182
3506
|
}));
|
|
3183
|
-
if (
|
|
3507
|
+
if (memo7 !== void 0) state.memo = memo7;
|
|
3184
3508
|
}
|
|
3185
3509
|
clean() {
|
|
3186
3510
|
this.eventStore.clean();
|
|
@@ -4529,11 +4853,11 @@ Please add \`${key}Action\` when creating your handler.`);
|
|
|
4529
4853
|
const startKey = handlerKey + "Start";
|
|
4530
4854
|
const endKey = handlerKey + "End";
|
|
4531
4855
|
const fn = (state) => {
|
|
4532
|
-
let
|
|
4856
|
+
let memo7 = void 0;
|
|
4533
4857
|
if (state.first && startKey in handlers) handlers[startKey](state);
|
|
4534
|
-
if (handlerKey in handlers)
|
|
4858
|
+
if (handlerKey in handlers) memo7 = handlers[handlerKey](state);
|
|
4535
4859
|
if (state.last && endKey in handlers) handlers[endKey](state);
|
|
4536
|
-
return
|
|
4860
|
+
return memo7;
|
|
4537
4861
|
};
|
|
4538
4862
|
internalHandlers[key] = fn;
|
|
4539
4863
|
config[key] = config[key] || {};
|
|
@@ -4585,8 +4909,8 @@ function useGesture(handlers, config) {
|
|
|
4585
4909
|
}
|
|
4586
4910
|
|
|
4587
4911
|
// src/lib/memo.ts
|
|
4588
|
-
var firstMemo = (first,
|
|
4589
|
-
if (!first && !
|
|
4912
|
+
var firstMemo = (first, memo7, initializer) => {
|
|
4913
|
+
if (!first && !memo7) {
|
|
4590
4914
|
throw new Error(
|
|
4591
4915
|
"Missing memo initialization. You likely forgot to return the result of `firstMemo` in the event function"
|
|
4592
4916
|
);
|
|
@@ -4594,7 +4918,7 @@ var firstMemo = (first, memo, initializer) => {
|
|
|
4594
4918
|
if (first) {
|
|
4595
4919
|
return initializer();
|
|
4596
4920
|
}
|
|
4597
|
-
return
|
|
4921
|
+
return memo7;
|
|
4598
4922
|
};
|
|
4599
4923
|
|
|
4600
4924
|
// src/hooks/viewport/useViewportContainer.tsx
|
|
@@ -4726,13 +5050,13 @@ var useViewportContainer = ({
|
|
|
4726
5050
|
useGesture(
|
|
4727
5051
|
{
|
|
4728
5052
|
onPinch: (state) => {
|
|
4729
|
-
const { origin: origin2, first, movement, delta, event, memo } = state;
|
|
5053
|
+
const { origin: origin2, first, movement, delta, event, memo: memo7 } = state;
|
|
4730
5054
|
const [ms] = movement;
|
|
4731
5055
|
const [deltaScale] = delta;
|
|
4732
5056
|
const currentElement = elementRef.current;
|
|
4733
5057
|
const currentContainer = containerRef.current;
|
|
4734
5058
|
if (!currentElement || !currentContainer) return;
|
|
4735
|
-
const newMemo = firstMemo(first,
|
|
5059
|
+
const newMemo = firstMemo(first, memo7, () => {
|
|
4736
5060
|
const elementRect = currentElement.getBoundingClientRect();
|
|
4737
5061
|
const containerRect = currentContainer.getBoundingClientRect();
|
|
4738
5062
|
const currentZoom = transformations.current.zoom;
|
|
@@ -4799,12 +5123,43 @@ var useViewportContainer = ({
|
|
|
4799
5123
|
origin
|
|
4800
5124
|
};
|
|
4801
5125
|
};
|
|
5126
|
+
var selectLargestPageWidth = (state) => state.viewports.reduce((max, vp) => Math.max(max, vp.width), 0);
|
|
4802
5127
|
var DEFAULT_HEIGHT = 600;
|
|
4803
5128
|
var EXTRA_HEIGHT = 0;
|
|
5129
|
+
var DEFAULT_VIRTUALIZER_OPTIONS = { overscan: 1 };
|
|
5130
|
+
var VirtualizedPageItem = memo(
|
|
5131
|
+
({ child, virtualItem, innerBoxWidth }) => {
|
|
5132
|
+
return /* @__PURE__ */ jsx(
|
|
5133
|
+
"div",
|
|
5134
|
+
{
|
|
5135
|
+
style: {
|
|
5136
|
+
width: innerBoxWidth,
|
|
5137
|
+
height: "0px"
|
|
5138
|
+
},
|
|
5139
|
+
children: /* @__PURE__ */ jsx(
|
|
5140
|
+
"div",
|
|
5141
|
+
{
|
|
5142
|
+
style: {
|
|
5143
|
+
height: `${virtualItem.size}px`,
|
|
5144
|
+
transform: `translateY(${virtualItem.start}px)`
|
|
5145
|
+
},
|
|
5146
|
+
children: cloneElement(child, {
|
|
5147
|
+
key: virtualItem.key,
|
|
5148
|
+
//@ts-expect-error pageNumber is not a valid react key
|
|
5149
|
+
pageNumber: virtualItem.index + 1
|
|
5150
|
+
})
|
|
5151
|
+
}
|
|
5152
|
+
)
|
|
5153
|
+
}
|
|
5154
|
+
);
|
|
5155
|
+
},
|
|
5156
|
+
(prev, next) => prev.child === next.child && prev.innerBoxWidth === next.innerBoxWidth && prev.virtualItem.key === next.virtualItem.key && prev.virtualItem.index === next.virtualItem.index && prev.virtualItem.size === next.virtualItem.size && prev.virtualItem.start === next.virtualItem.start
|
|
5157
|
+
);
|
|
5158
|
+
VirtualizedPageItem.displayName = "VirtualizedPageItem";
|
|
4804
5159
|
var Pages = ({
|
|
4805
5160
|
children,
|
|
4806
5161
|
gap = 10,
|
|
4807
|
-
virtualizerOptions =
|
|
5162
|
+
virtualizerOptions = DEFAULT_VIRTUALIZER_OPTIONS,
|
|
4808
5163
|
initialOffset,
|
|
4809
5164
|
onOffsetChange,
|
|
4810
5165
|
...props
|
|
@@ -4824,12 +5179,16 @@ var Pages = ({
|
|
|
4824
5179
|
const setVirtualizer = usePdf((state) => state.setVirtualizer);
|
|
4825
5180
|
const { scrollToFn } = useScrollFn();
|
|
4826
5181
|
const { observeElementOffset } = useObserveElement();
|
|
5182
|
+
const viewportsRef = useRef(viewports);
|
|
5183
|
+
viewportsRef.current = viewports;
|
|
4827
5184
|
const estimateSize = useCallback(
|
|
4828
5185
|
(index) => {
|
|
4829
|
-
|
|
4830
|
-
|
|
5186
|
+
const vp = viewportsRef.current;
|
|
5187
|
+
if (!vp || !vp[index]) return DEFAULT_HEIGHT;
|
|
5188
|
+
return vp[index].height + EXTRA_HEIGHT;
|
|
4831
5189
|
},
|
|
4832
|
-
[
|
|
5190
|
+
[]
|
|
5191
|
+
// Stable — reads from ref
|
|
4833
5192
|
);
|
|
4834
5193
|
const virtualizer = useVirtualizer({
|
|
4835
5194
|
count: numPages || 0,
|
|
@@ -4869,9 +5228,7 @@ var Pages = ({
|
|
|
4869
5228
|
items
|
|
4870
5229
|
});
|
|
4871
5230
|
useFitWidth({ viewportRef: containerRef });
|
|
4872
|
-
const largestPageWidth = usePdf(
|
|
4873
|
-
(state) => Math.max(...state.viewports.map((v) => v.width))
|
|
4874
|
-
);
|
|
5231
|
+
const largestPageWidth = usePdf(selectLargestPageWidth);
|
|
4875
5232
|
useEffect(() => {
|
|
4876
5233
|
virtualizer.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
|
|
4877
5234
|
const size = virtualizer.getSize();
|
|
@@ -4931,26 +5288,11 @@ var Pages = ({
|
|
|
4931
5288
|
const innerBoxWidth = viewports?.[virtualItem.index] ? viewports[virtualItem.index]?.width : 0;
|
|
4932
5289
|
if (!innerBoxWidth) return null;
|
|
4933
5290
|
return /* @__PURE__ */ jsx(
|
|
4934
|
-
|
|
5291
|
+
VirtualizedPageItem,
|
|
4935
5292
|
{
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
},
|
|
4940
|
-
children: /* @__PURE__ */ jsx(
|
|
4941
|
-
"div",
|
|
4942
|
-
{
|
|
4943
|
-
style: {
|
|
4944
|
-
height: `${virtualItem.size}px`,
|
|
4945
|
-
transform: `translateY(${virtualItem.start}px)`
|
|
4946
|
-
},
|
|
4947
|
-
children: cloneElement(children, {
|
|
4948
|
-
key: virtualItem.key,
|
|
4949
|
-
//@ts-expect-error pageNumber is not a valid react key
|
|
4950
|
-
pageNumber: virtualItem.index + 1
|
|
4951
|
-
})
|
|
4952
|
-
}
|
|
4953
|
-
)
|
|
5293
|
+
child: children,
|
|
5294
|
+
virtualItem,
|
|
5295
|
+
innerBoxWidth
|
|
4954
5296
|
},
|
|
4955
5297
|
virtualItem.key
|
|
4956
5298
|
);
|
|
@@ -4962,17 +5304,44 @@ var Pages = ({
|
|
|
4962
5304
|
}
|
|
4963
5305
|
);
|
|
4964
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
|
+
}
|
|
4965
5331
|
var usePDFDocumentContext = ({
|
|
4966
5332
|
onDocumentLoad,
|
|
4967
5333
|
source,
|
|
4968
5334
|
initialRotation = 0,
|
|
4969
5335
|
isZoomFitWidth,
|
|
4970
5336
|
zoom = 1,
|
|
4971
|
-
zoomOptions
|
|
5337
|
+
zoomOptions,
|
|
5338
|
+
documentOptions
|
|
4972
5339
|
}) => {
|
|
4973
5340
|
const [_, setProgress] = useState(0);
|
|
4974
5341
|
const [initialState, setInitialState] = useState();
|
|
4975
5342
|
const [rotation] = useState(initialRotation);
|
|
5343
|
+
const documentOptionsRef = useRef(documentOptions);
|
|
5344
|
+
documentOptionsRef.current = documentOptions;
|
|
4976
5345
|
useEffect(() => {
|
|
4977
5346
|
const generateViewports = async (pdf) => {
|
|
4978
5347
|
const pageProxies = [];
|
|
@@ -4990,9 +5359,9 @@ var usePDFDocumentContext = ({
|
|
|
4990
5359
|
return viewport;
|
|
4991
5360
|
})
|
|
4992
5361
|
);
|
|
4993
|
-
const sortedPageProxies = pageProxies.
|
|
4994
|
-
|
|
4995
|
-
|
|
5362
|
+
const sortedPageProxies = pageProxies.toSorted(
|
|
5363
|
+
(a, b) => a.pageNumber - b.pageNumber
|
|
5364
|
+
);
|
|
4996
5365
|
setInitialState((prev) => ({
|
|
4997
5366
|
...prev,
|
|
4998
5367
|
isZoomFitWidth,
|
|
@@ -5006,28 +5375,46 @@ var usePDFDocumentContext = ({
|
|
|
5006
5375
|
const loadDocument = () => {
|
|
5007
5376
|
setInitialState(null);
|
|
5008
5377
|
setProgress(0);
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5378
|
+
let loadingTask = null;
|
|
5379
|
+
let isDisposed = false;
|
|
5380
|
+
void loadPdfJs().then(({ getDocument }) => {
|
|
5381
|
+
if (isDisposed) {
|
|
5012
5382
|
return;
|
|
5013
5383
|
}
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5384
|
+
loadingTask = getDocument(
|
|
5385
|
+
buildDocumentInitParams(source, documentOptionsRef.current)
|
|
5386
|
+
);
|
|
5387
|
+
loadingTask.onProgress = (progressEvent) => {
|
|
5388
|
+
if (progressEvent.loaded === progressEvent.total) {
|
|
5389
|
+
return;
|
|
5390
|
+
}
|
|
5391
|
+
setProgress(progressEvent.loaded / progressEvent.total);
|
|
5392
|
+
};
|
|
5393
|
+
return loadingTask.promise.then((proxy) => {
|
|
5394
|
+
if (isDisposed || loadingTask?.destroyed) {
|
|
5395
|
+
return;
|
|
5396
|
+
}
|
|
5397
|
+
onDocumentLoad?.({ proxy, source });
|
|
5398
|
+
setProgress(1);
|
|
5399
|
+
return generateViewports(proxy);
|
|
5400
|
+
}).catch((error) => {
|
|
5401
|
+
if (isDisposed || loadingTask?.destroyed) {
|
|
5402
|
+
return;
|
|
5403
|
+
}
|
|
5404
|
+
console.error("Error loading PDF document", error);
|
|
5405
|
+
});
|
|
5020
5406
|
}).catch((error) => {
|
|
5021
|
-
if (
|
|
5407
|
+
if (isDisposed) {
|
|
5022
5408
|
return;
|
|
5023
5409
|
}
|
|
5024
|
-
console.error("Error loading PDF
|
|
5410
|
+
console.error("Error loading PDF.js", error);
|
|
5025
5411
|
});
|
|
5026
5412
|
return () => {
|
|
5027
|
-
|
|
5413
|
+
isDisposed = true;
|
|
5414
|
+
void loadingTask?.destroy();
|
|
5028
5415
|
};
|
|
5029
5416
|
};
|
|
5030
|
-
loadDocument();
|
|
5417
|
+
return loadDocument();
|
|
5031
5418
|
}, [source]);
|
|
5032
5419
|
return {
|
|
5033
5420
|
initialState
|
|
@@ -5042,6 +5429,7 @@ var Root = forwardRef(
|
|
|
5042
5429
|
isZoomFitWidth,
|
|
5043
5430
|
zoom,
|
|
5044
5431
|
zoomOptions,
|
|
5432
|
+
documentOptions,
|
|
5045
5433
|
...props
|
|
5046
5434
|
}, ref) => {
|
|
5047
5435
|
const { initialState } = usePDFDocumentContext({
|
|
@@ -5049,12 +5437,20 @@ var Root = forwardRef(
|
|
|
5049
5437
|
onDocumentLoad,
|
|
5050
5438
|
isZoomFitWidth,
|
|
5051
5439
|
zoom,
|
|
5052
|
-
zoomOptions
|
|
5440
|
+
zoomOptions,
|
|
5441
|
+
documentOptions
|
|
5053
5442
|
});
|
|
5054
5443
|
const linkService = useCreatePDFLinkService(
|
|
5055
5444
|
initialState?.pdfDocumentProxy ?? null
|
|
5056
5445
|
);
|
|
5057
|
-
|
|
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..." });
|
|
5058
5454
|
}
|
|
5059
5455
|
);
|
|
5060
5456
|
Root.displayName = "Root";
|
|
@@ -5104,8 +5500,8 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5104
5500
|
const isVisible = isFirstPage || debouncedVisible;
|
|
5105
5501
|
useEffect(() => {
|
|
5106
5502
|
const renderThumbnail = async () => {
|
|
5107
|
-
const
|
|
5108
|
-
if (!
|
|
5503
|
+
const canvas2 = canvasRef.current;
|
|
5504
|
+
if (!canvas2 || !pageProxy) return;
|
|
5109
5505
|
try {
|
|
5110
5506
|
if (renderTaskRef.current) {
|
|
5111
5507
|
renderTaskRef.current.cancel();
|
|
@@ -5113,11 +5509,12 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5113
5509
|
const viewport = pageProxy.getViewport({ scale: 1 });
|
|
5114
5510
|
const scale = Math.min(maxWidth / viewport.width, maxHeight / viewport.height) * (isVisible ? dpr : 0.5);
|
|
5115
5511
|
const scaledViewport = pageProxy.getViewport({ scale });
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
const context =
|
|
5512
|
+
canvas2.width = scaledViewport.width;
|
|
5513
|
+
canvas2.height = scaledViewport.height;
|
|
5514
|
+
const context = canvas2.getContext("2d");
|
|
5119
5515
|
if (!context) return;
|
|
5120
5516
|
const renderTask = pageProxy.render({
|
|
5517
|
+
canvas: canvas2,
|
|
5121
5518
|
canvasContext: context,
|
|
5122
5519
|
viewport: scaledViewport
|
|
5123
5520
|
});
|
|
@@ -5132,8 +5529,13 @@ var useThumbnail = (pageNumber, config = {}) => {
|
|
|
5132
5529
|
}
|
|
5133
5530
|
};
|
|
5134
5531
|
renderThumbnail();
|
|
5532
|
+
const canvas = canvasRef.current;
|
|
5135
5533
|
return () => {
|
|
5136
5534
|
renderTaskRef.current?.cancel();
|
|
5535
|
+
if (canvas) {
|
|
5536
|
+
canvas.width = 1;
|
|
5537
|
+
canvas.height = 1;
|
|
5538
|
+
}
|
|
5137
5539
|
};
|
|
5138
5540
|
}, [pageProxy, isVisible, dpr, maxHeight, maxWidth]);
|
|
5139
5541
|
return {
|
|
@@ -5212,12 +5614,12 @@ var ZoomOut = ({ ...props }) => {
|
|
|
5212
5614
|
);
|
|
5213
5615
|
};
|
|
5214
5616
|
var CurrentZoom = ({ ...props }) => {
|
|
5215
|
-
const
|
|
5617
|
+
const updateZoom = usePdf((state) => state.updateZoom);
|
|
5216
5618
|
const realZoom = usePdf((state) => state.zoom);
|
|
5217
5619
|
const [zoom, setZoom] = useState((realZoom * 100).toFixed(0));
|
|
5218
|
-
const
|
|
5620
|
+
const isInputFocused = useRef(false);
|
|
5219
5621
|
useEffect(() => {
|
|
5220
|
-
if (
|
|
5622
|
+
if (isInputFocused.current) {
|
|
5221
5623
|
return;
|
|
5222
5624
|
}
|
|
5223
5625
|
setZoom((realZoom * 100).toFixed(0));
|
|
@@ -5228,14 +5630,14 @@ var CurrentZoom = ({ ...props }) => {
|
|
|
5228
5630
|
...props,
|
|
5229
5631
|
value: zoom,
|
|
5230
5632
|
onClick: () => {
|
|
5231
|
-
|
|
5633
|
+
isInputFocused.current = true;
|
|
5232
5634
|
},
|
|
5233
5635
|
onChange: (e) => {
|
|
5234
|
-
|
|
5636
|
+
updateZoom(Number(e.target.value) / 100);
|
|
5235
5637
|
setZoom(e.target.value);
|
|
5236
5638
|
},
|
|
5237
5639
|
onBlur: () => {
|
|
5238
|
-
|
|
5640
|
+
isInputFocused.current = false;
|
|
5239
5641
|
setZoom((realZoom * 100).toFixed(0));
|
|
5240
5642
|
}
|
|
5241
5643
|
}
|
|
@@ -5468,4 +5870,7 @@ function mergeAdjacentRects(rects) {
|
|
|
5468
5870
|
return merged;
|
|
5469
5871
|
}
|
|
5470
5872
|
|
|
5471
|
-
|
|
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 };
|