kviewer 0.2.0 → 0.2.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/README.md +47 -0
- package/dist/module.d.mts +8 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +25 -3
- package/dist/runtime/annotation/engine/canvas-resolution.d.ts +7 -0
- package/dist/runtime/annotation/engine/canvas-resolution.js +21 -0
- package/dist/runtime/annotation/engine/editor/editor.d.ts +5 -0
- package/dist/runtime/annotation/engine/editor/editor.js +14 -0
- package/dist/runtime/annotation/engine/editor/selector.d.ts +13 -1
- package/dist/runtime/annotation/engine/editor/selector.js +60 -8
- package/dist/runtime/annotation/engine/painter.d.ts +31 -0
- package/dist/runtime/annotation/engine/painter.js +88 -2
- package/dist/runtime/annotation/engine/store.js +3 -0
- package/dist/runtime/annotation/engine/tools/arrow.js +0 -1
- package/dist/runtime/annotation/engine/tools/circle.js +0 -2
- package/dist/runtime/annotation/engine/tools/free-highlight.js +0 -2
- package/dist/runtime/annotation/engine/tools/freehand.js +0 -2
- package/dist/runtime/annotation/engine/tools/rectangle.js +0 -2
- package/dist/runtime/annotation/engine/types.d.ts +5 -0
- package/dist/runtime/annotation/font-style.d.ts +15 -0
- package/dist/runtime/annotation/font-style.js +19 -0
- package/dist/runtime/annotation/parsers/extractCheckboxStyles.d.ts +8 -0
- package/dist/runtime/annotation/parsers/extractCheckboxStyles.js +24 -1
- package/dist/runtime/annotation/pdf-export/annotation-flags.d.ts +8 -0
- package/dist/runtime/annotation/pdf-export/annotation-flags.js +5 -0
- package/dist/runtime/annotation/pdf-export/export-form-fields.js +3 -1
- package/dist/runtime/annotation/pdf-export/export.js +13 -1
- package/dist/runtime/annotation/pdf-export/parse_circle.js +3 -0
- package/dist/runtime/annotation/pdf-export/parse_freetext.js +31 -19
- package/dist/runtime/annotation/pdf-export/parse_highlight.js +3 -0
- package/dist/runtime/annotation/pdf-export/parse_ink.js +5 -1
- package/dist/runtime/annotation/pdf-export/parse_line.js +5 -1
- package/dist/runtime/annotation/pdf-export/parse_polyline.js +5 -1
- package/dist/runtime/annotation/pdf-export/parse_square.js +3 -0
- package/dist/runtime/annotation/pdf-export/parse_stamp.js +4 -2
- package/dist/runtime/annotation/pdf-export/parse_strikeout.js +3 -0
- package/dist/runtime/annotation/pdf-export/parse_text.js +3 -0
- package/dist/runtime/annotation/pdf-export/parse_underline.js +3 -0
- package/dist/runtime/annotation/pdf-export/unicode-font.d.ts +5 -0
- package/dist/runtime/annotation/pdf-export/unicode-font.js +31 -0
- package/dist/runtime/annotation/pdf-export/winansi.d.ts +17 -0
- package/dist/runtime/annotation/pdf-export/winansi.js +77 -0
- package/dist/runtime/annotation/pdf-import/decode_circle.js +0 -1
- package/dist/runtime/annotation/pdf-import/decode_ink.js +0 -1
- package/dist/runtime/annotation/pdf-import/decode_line.js +0 -1
- package/dist/runtime/annotation/pdf-import/decode_square.js +0 -1
- package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.d.ts +12 -0
- package/dist/runtime/annotation/pdf-import/extract_stamp_appearance.js +17 -4
- package/dist/runtime/annotation/pdf-import/types.d.ts +2 -0
- package/dist/runtime/assets/fonts/LICENSE-LiberationSans +102 -0
- package/dist/runtime/assets/fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/runtime/assets/kviewer.css +1 -1
- package/dist/runtime/components/AnnotationToolbar.vue +6 -4
- package/dist/runtime/components/PdfPage.d.vue.ts +16 -0
- package/dist/runtime/components/PdfPage.vue +170 -9
- package/dist/runtime/components/PdfPage.vue.d.ts +16 -0
- package/dist/runtime/components/PerfHud.d.vue.ts +3 -0
- package/dist/runtime/components/PerfHud.vue +97 -0
- package/dist/runtime/components/PerfHud.vue.d.ts +3 -0
- package/dist/runtime/components/ToolButton.vue +4 -1
- package/dist/runtime/components/Viewer.d.vue.ts +37 -2
- package/dist/runtime/components/Viewer.vue +208 -13
- package/dist/runtime/components/Viewer.vue.d.ts +37 -2
- package/dist/runtime/components/ViewerBar.d.vue.ts +21 -1
- package/dist/runtime/components/ViewerBar.vue +183 -101
- package/dist/runtime/components/ViewerBar.vue.d.ts +21 -1
- package/dist/runtime/components/ViewerTabs.d.vue.ts +10 -2
- package/dist/runtime/components/ViewerTabs.vue +13 -2
- package/dist/runtime/components/ViewerTabs.vue.d.ts +10 -2
- package/dist/runtime/components/form-fields/FormDropdown.vue +50 -19
- package/dist/runtime/components/form-fields/FormSignatureField.vue +4 -2
- package/dist/runtime/components/form-fields/FormTextField.vue +7 -18
- package/dist/runtime/components/modals/FreeTextModal.vue +6 -4
- package/dist/runtime/components/modals/SignatureDrawModal.vue +6 -4
- package/dist/runtime/components/panels/PlacedFieldSidebar.vue +67 -50
- package/dist/runtime/components/panels/SignaturePicker.vue +6 -4
- package/dist/runtime/components/panels/StampPicker.vue +3 -1
- package/dist/runtime/components/tools/AnnotationToolButton.d.vue.ts +10 -0
- package/dist/runtime/components/tools/AnnotationToolButton.vue +33 -0
- package/dist/runtime/components/tools/AnnotationToolButton.vue.d.ts +10 -0
- package/dist/runtime/components/tools/MarqueeTool.vue +3 -1
- package/dist/runtime/components/tools/PageSettings.vue +13 -11
- package/dist/runtime/components/tools/SearchTool.vue +12 -10
- package/dist/runtime/components/tools/ToolProperties.vue +7 -5
- package/dist/runtime/components/tools/ToolbarMenu.d.vue.ts +9 -0
- package/dist/runtime/components/tools/ToolbarMenu.vue +79 -0
- package/dist/runtime/components/tools/ToolbarMenu.vue.d.ts +9 -0
- package/dist/runtime/components/tools/ZoomControls.vue +17 -9
- package/dist/runtime/composables/canvas-limits.d.ts +22 -0
- package/dist/runtime/composables/canvas-limits.js +10 -0
- package/dist/runtime/composables/useFormFields.d.ts +6 -0
- package/dist/runtime/composables/useFormFields.js +28 -0
- package/dist/runtime/composables/useInertiaPanzoom.d.ts +10 -0
- package/dist/runtime/composables/useInertiaPanzoom.js +57 -10
- package/dist/runtime/composables/useKviewerI18n.d.ts +18 -0
- package/dist/runtime/composables/useKviewerI18n.js +29 -0
- package/dist/runtime/composables/usePageVirtualization.d.ts +11 -0
- package/dist/runtime/composables/usePageVirtualization.js +40 -0
- package/dist/runtime/composables/useScriptingManager.d.ts +8 -4
- package/dist/runtime/composables/useScriptingManager.js +2 -0
- package/dist/runtime/composables/useViewerState.d.ts +4 -0
- package/dist/runtime/composables/useViewerState.js +8 -0
- package/dist/runtime/embed/bridge-client.d.ts +9 -1
- package/dist/runtime/embed/bridge-client.js +13 -0
- package/dist/runtime/embed/bridge-host.d.ts +3 -0
- package/dist/runtime/embed/bridge-host.js +30 -8
- package/dist/runtime/embed/protocol.d.ts +21 -0
- package/dist/runtime/i18n/messages.d.ts +252 -0
- package/dist/runtime/i18n/messages.js +249 -0
- package/dist/runtime/public-types.d.ts +2 -0
- package/dist/runtime/toolbar-tools.d.ts +54 -0
- package/dist/runtime/toolbar-tools.js +79 -0
- package/dist/types.d.mts +1 -1
- package/package.json +14 -31
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
<div ref="viewerRoot" class="flex flex-col h-full">
|
|
3
3
|
<!-- Header slot: defaults to ViewerBar -->
|
|
4
4
|
<slot name="header">
|
|
5
|
-
<ViewerBar :menu-items="props.menuItems"
|
|
5
|
+
<ViewerBar :menu-items="props.menuItems" :tools="props.tools">
|
|
6
|
+
<template v-for="name in toolSlotNames" :key="name" #[name]="scope">
|
|
7
|
+
<slot :name="name" v-bind="scope" />
|
|
8
|
+
</template>
|
|
9
|
+
</ViewerBar>
|
|
6
10
|
</slot>
|
|
7
11
|
|
|
8
12
|
<div class="flex flex-1 min-h-0">
|
|
@@ -61,6 +65,7 @@
|
|
|
61
65
|
:page-number="singlePageMeta.pageNumber"
|
|
62
66
|
:page-meta="singlePageMeta"
|
|
63
67
|
:scale="pageScale"
|
|
68
|
+
:view-window="pageViewWindow(singlePageMeta.pageNumber)"
|
|
64
69
|
/>
|
|
65
70
|
</div>
|
|
66
71
|
</div>
|
|
@@ -89,6 +94,7 @@
|
|
|
89
94
|
:page-number="meta.pageNumber"
|
|
90
95
|
:page-meta="meta"
|
|
91
96
|
:scale="pageScale"
|
|
97
|
+
:view-window="pageViewWindow(meta.pageNumber)"
|
|
92
98
|
/>
|
|
93
99
|
</div>
|
|
94
100
|
</div>
|
|
@@ -122,6 +128,7 @@
|
|
|
122
128
|
:page-number="meta.pageNumber"
|
|
123
129
|
:page-meta="meta"
|
|
124
130
|
:scale="pageScale"
|
|
131
|
+
:view-window="pageViewWindow(meta.pageNumber)"
|
|
125
132
|
/>
|
|
126
133
|
</div>
|
|
127
134
|
</div>
|
|
@@ -133,6 +140,10 @@
|
|
|
133
140
|
|
|
134
141
|
<ClientOnly>
|
|
135
142
|
<FloatingPageIndicator :scroll-container="scrollContainer" />
|
|
143
|
+
<!-- Debug overlay for on-device profiling; renders only with
|
|
144
|
+
?perf in the URL. Gated on `active` so tabbed hosts show a
|
|
145
|
+
single instance. -->
|
|
146
|
+
<PerfHud v-if="props.active" />
|
|
136
147
|
</ClientOnly>
|
|
137
148
|
</div>
|
|
138
149
|
|
|
@@ -181,14 +192,17 @@ import {
|
|
|
181
192
|
nextTick,
|
|
182
193
|
triggerRef,
|
|
183
194
|
onMounted,
|
|
184
|
-
onBeforeUnmount
|
|
195
|
+
onBeforeUnmount,
|
|
196
|
+
useSlots
|
|
185
197
|
} from "vue";
|
|
198
|
+
import { useRuntimeConfig } from "#imports";
|
|
186
199
|
import ViewerBar from "./ViewerBar.vue";
|
|
187
200
|
import PdfPage from "./PdfPage.vue";
|
|
188
201
|
import FreeTextModal from "./modals/FreeTextModal.vue";
|
|
189
202
|
import PlacedFieldSidebar from "./panels/PlacedFieldSidebar.vue";
|
|
190
203
|
import AnnotationToolbar from "./AnnotationToolbar.vue";
|
|
191
204
|
import FloatingPageIndicator from "./FloatingPageIndicator.vue";
|
|
205
|
+
import PerfHud from "./PerfHud.vue";
|
|
192
206
|
import { providePageSettings } from "../composables/usePageSettings";
|
|
193
207
|
import { mapViewModeToFitMode, normalizeZoom, provideViewerState } from "../composables/useViewerState";
|
|
194
208
|
import { provideViewerSearch } from "../composables/useViewerSearch";
|
|
@@ -206,11 +220,13 @@ import { createPageProxyCache } from "../composables/usePageProxyCache";
|
|
|
206
220
|
import { createSearchIndex } from "../composables/useSearchIndex";
|
|
207
221
|
import { createShapeDetection } from "../composables/useShapeDetection";
|
|
208
222
|
import { provideFormFields } from "../composables/useFormFields";
|
|
209
|
-
import { extractCheckboxStyles, extractButtonCaptions } from "../annotation/parsers/extractCheckboxStyles";
|
|
223
|
+
import { extractCheckboxStyles, extractButtonCaptions, extractSignedSignatureNames } from "../annotation/parsers/extractCheckboxStyles";
|
|
210
224
|
import { isIPad } from "../annotation/engine/input-device";
|
|
211
225
|
import { useInertiaPanzoom } from "../composables/useInertiaPanzoom";
|
|
212
226
|
import { createScriptingManager } from "../composables/useScriptingManager";
|
|
213
227
|
import { createScriptingBridge } from "../composables/useScriptingBridge";
|
|
228
|
+
import { provideKviewerI18n } from "../composables/useKviewerI18n";
|
|
229
|
+
import { KVIEWER_LOCALES } from "../i18n/messages";
|
|
214
230
|
const props = defineProps({
|
|
215
231
|
source: { type: null, required: true },
|
|
216
232
|
textLayer: { type: Boolean, required: false },
|
|
@@ -227,12 +243,19 @@ const props = defineProps({
|
|
|
227
243
|
roleColors: { type: Object, required: false, default: void 0 },
|
|
228
244
|
activeRoleId: { type: [String, null], required: false, default: void 0 },
|
|
229
245
|
scripting: { type: Boolean, required: false, default: false },
|
|
230
|
-
menuItems: { type: Array, required: false, default: void 0 }
|
|
246
|
+
menuItems: { type: Array, required: false, default: void 0 },
|
|
247
|
+
tools: { type: Array, required: false, default: void 0 },
|
|
248
|
+
locale: { type: null, required: false, default: void 0 },
|
|
249
|
+
messages: { type: Object, required: false, default: void 0 }
|
|
231
250
|
});
|
|
232
|
-
const
|
|
251
|
+
const slots = useSlots();
|
|
252
|
+
const toolSlotNames = computed(
|
|
253
|
+
() => Object.keys(slots).filter((name) => name.startsWith("tool-"))
|
|
254
|
+
);
|
|
255
|
+
const emit = defineEmits(["update:formEditMode", "update:activeRoleId", "update:viewedPages", "all-pages-read"]);
|
|
233
256
|
const viewerRoot = ref(null);
|
|
234
257
|
const scrollContainer = ref(null);
|
|
235
|
-
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn } = provideViewerState();
|
|
258
|
+
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn, setApplyScaleFn } = provideViewerState();
|
|
236
259
|
watchEffect(() => {
|
|
237
260
|
viewerState.readonly.value = props.readonly ?? false;
|
|
238
261
|
});
|
|
@@ -255,6 +278,14 @@ watch(
|
|
|
255
278
|
);
|
|
256
279
|
const viewerSearch = provideViewerSearch();
|
|
257
280
|
const formFieldsState = provideFormFields();
|
|
281
|
+
const kviewerPublicConfig = useRuntimeConfig().public.kviewer;
|
|
282
|
+
provideKviewerI18n(
|
|
283
|
+
computed(() => {
|
|
284
|
+
const candidate = props.locale ?? kviewerPublicConfig?.locale ?? "en";
|
|
285
|
+
return KVIEWER_LOCALES.includes(candidate) ? candidate : "en";
|
|
286
|
+
}),
|
|
287
|
+
computed(() => props.messages ?? {})
|
|
288
|
+
);
|
|
258
289
|
watch(
|
|
259
290
|
() => props.roleColors,
|
|
260
291
|
(v) => {
|
|
@@ -295,6 +326,7 @@ const proxyCache = createPageProxyCache();
|
|
|
295
326
|
const searchIndex = createSearchIndex();
|
|
296
327
|
const shapeDetectionCache = createShapeDetection();
|
|
297
328
|
const doc = shallowRef(null);
|
|
329
|
+
let destroyActiveDoc = null;
|
|
298
330
|
let painter = null;
|
|
299
331
|
let scriptingManager = null;
|
|
300
332
|
let scriptingBridge = null;
|
|
@@ -306,6 +338,31 @@ watch(
|
|
|
306
338
|
viewerState.currentPage.value = page;
|
|
307
339
|
}
|
|
308
340
|
);
|
|
341
|
+
watch(
|
|
342
|
+
() => viewerState.currentPage.value,
|
|
343
|
+
(page) => virtualization.markPageViewed(page),
|
|
344
|
+
{ immediate: true }
|
|
345
|
+
);
|
|
346
|
+
watch(
|
|
347
|
+
() => pageMetas.value.length,
|
|
348
|
+
(len) => {
|
|
349
|
+
if (len > 0 && !isSinglePageMode.value) {
|
|
350
|
+
nextTick(() => virtualization.updateCurrentPageByRect());
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
watch(
|
|
355
|
+
virtualization.allPagesRead,
|
|
356
|
+
(read, prev) => {
|
|
357
|
+
if (read && !prev) emit("all-pages-read");
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
watch(
|
|
361
|
+
virtualization.viewedPages,
|
|
362
|
+
(pages) => {
|
|
363
|
+
emit("update:viewedPages", [...pages].sort((a, b) => a - b));
|
|
364
|
+
}
|
|
365
|
+
);
|
|
309
366
|
function onPageRef(pageNumber, el) {
|
|
310
367
|
if (el) {
|
|
311
368
|
virtualization.observePage(pageNumber, el);
|
|
@@ -328,6 +385,116 @@ const isRotatedSideways = computed(() => {
|
|
|
328
385
|
});
|
|
329
386
|
const zoomWrapper = ref(null);
|
|
330
387
|
const inertiaPanzoom = useInertiaPanzoom({ minScale: 0.5, maxScale: 4 });
|
|
388
|
+
const MIN_ZOOM = 0.25;
|
|
389
|
+
const MAX_ZOOM = 4;
|
|
390
|
+
let suppressScaleReset = false;
|
|
391
|
+
async function commitZoom(target) {
|
|
392
|
+
const s0 = viewerState.scale.value;
|
|
393
|
+
target = Math.min(Math.max(MAX_ZOOM, s0), Math.max(Math.min(MIN_ZOOM, s0), target));
|
|
394
|
+
const viewportEl = zoomWrapper.value?.parentElement ?? null;
|
|
395
|
+
const anchor = virtualization.getPageElement(viewerState.currentPage.value);
|
|
396
|
+
const visual = inertiaPanzoom.getScale();
|
|
397
|
+
if (target === viewerState.scale.value && Math.abs(visual - 1) < 5e-3) return;
|
|
398
|
+
if (!viewportEl || !anchor || !anchor.isConnected) {
|
|
399
|
+
viewerState.fitMode.value = "custom";
|
|
400
|
+
viewerState.scale.value = target;
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const vpRect = viewportEl.getBoundingClientRect();
|
|
404
|
+
const fx = vpRect.width / 2;
|
|
405
|
+
const fy = vpRect.height / 2;
|
|
406
|
+
const aRect = anchor.getBoundingClientRect();
|
|
407
|
+
const qx = (vpRect.left + fx - aRect.left) / aRect.width;
|
|
408
|
+
const qy = (vpRect.top + fy - aRect.top) / aRect.height;
|
|
409
|
+
suppressScaleReset = target !== viewerState.scale.value;
|
|
410
|
+
viewerState.fitMode.value = "custom";
|
|
411
|
+
viewerState.scale.value = target;
|
|
412
|
+
await nextTick();
|
|
413
|
+
const t = inertiaPanzoom.getTransform();
|
|
414
|
+
const vp2 = viewportEl.getBoundingClientRect();
|
|
415
|
+
const a2 = anchor.getBoundingClientRect();
|
|
416
|
+
const layoutLeft = (a2.left - vp2.left - t.tx) / t.scale;
|
|
417
|
+
const layoutTop = (a2.top - vp2.top - t.ty) / t.scale;
|
|
418
|
+
const layoutW = a2.width / t.scale;
|
|
419
|
+
const layoutH = a2.height / t.scale;
|
|
420
|
+
inertiaPanzoom.setTransform({
|
|
421
|
+
scale: 1,
|
|
422
|
+
tx: fx - (layoutLeft + qx * layoutW),
|
|
423
|
+
ty: fy - (layoutTop + qy * layoutH)
|
|
424
|
+
});
|
|
425
|
+
viewWindows.value = {};
|
|
426
|
+
computeViewWindows();
|
|
427
|
+
}
|
|
428
|
+
setApplyScaleFn((value) => {
|
|
429
|
+
void commitZoom(value);
|
|
430
|
+
});
|
|
431
|
+
watch(
|
|
432
|
+
() => viewerState.scale.value,
|
|
433
|
+
(s) => inertiaPanzoom.setScaleLimits(
|
|
434
|
+
Math.min(1, MIN_ZOOM / s),
|
|
435
|
+
Math.max(1, MAX_ZOOM / s)
|
|
436
|
+
),
|
|
437
|
+
{ immediate: true }
|
|
438
|
+
);
|
|
439
|
+
inertiaPanzoom.onZoomEnd((visual) => {
|
|
440
|
+
if (Math.abs(visual - 1) < 5e-3) return;
|
|
441
|
+
void commitZoom(viewerState.scale.value * visual);
|
|
442
|
+
});
|
|
443
|
+
const viewWindows = shallowRef({});
|
|
444
|
+
const VIEW_WINDOW_MARGIN = 0.25;
|
|
445
|
+
function computeViewWindows() {
|
|
446
|
+
const viewportEl = zoomWrapper.value?.parentElement;
|
|
447
|
+
if (!viewportEl) return;
|
|
448
|
+
const vpRect = viewportEl.getBoundingClientRect();
|
|
449
|
+
if (vpRect.width === 0 || vpRect.height === 0) return;
|
|
450
|
+
const prev = viewWindows.value;
|
|
451
|
+
const next = {};
|
|
452
|
+
let changed = false;
|
|
453
|
+
for (const meta of pageMetas.value) {
|
|
454
|
+
const el = virtualization.getPageElement(meta.pageNumber);
|
|
455
|
+
if (!el) continue;
|
|
456
|
+
const r = el.getBoundingClientRect();
|
|
457
|
+
if (r.width === 0 || el.offsetWidth === 0) continue;
|
|
458
|
+
const k = r.width / el.offsetWidth;
|
|
459
|
+
const vx = (Math.max(vpRect.left, r.left) - r.left) / k;
|
|
460
|
+
const vy = (Math.max(vpRect.top, r.top) - r.top) / k;
|
|
461
|
+
const vr = (Math.min(vpRect.right, r.right) - r.left) / k;
|
|
462
|
+
const vb = (Math.min(vpRect.bottom, r.bottom) - r.top) / k;
|
|
463
|
+
if (vr <= vx || vb <= vy) continue;
|
|
464
|
+
const old = prev[meta.pageNumber];
|
|
465
|
+
if (old && vx >= old.x && vy >= old.y && vr <= old.x + old.width && vb <= old.y + old.height) {
|
|
466
|
+
next[meta.pageNumber] = old;
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
const mx = vpRect.width / k * VIEW_WINDOW_MARGIN;
|
|
470
|
+
const my = vpRect.height / k * VIEW_WINDOW_MARGIN;
|
|
471
|
+
next[meta.pageNumber] = {
|
|
472
|
+
x: vx - mx,
|
|
473
|
+
y: vy - my,
|
|
474
|
+
width: vr - vx + 2 * mx,
|
|
475
|
+
height: vb - vy + 2 * my
|
|
476
|
+
};
|
|
477
|
+
changed = true;
|
|
478
|
+
}
|
|
479
|
+
if (changed || Object.keys(next).length !== Object.keys(prev).length) {
|
|
480
|
+
viewWindows.value = next;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
let viewWindowTimer = 0;
|
|
484
|
+
let lastViewWindowUpdate = 0;
|
|
485
|
+
inertiaPanzoom.onChange(() => {
|
|
486
|
+
const now = performance.now();
|
|
487
|
+
const wait = Math.max(0, 250 - (now - lastViewWindowUpdate));
|
|
488
|
+
clearTimeout(viewWindowTimer);
|
|
489
|
+
viewWindowTimer = window.setTimeout(() => {
|
|
490
|
+
lastViewWindowUpdate = performance.now();
|
|
491
|
+
computeViewWindows();
|
|
492
|
+
}, wait);
|
|
493
|
+
});
|
|
494
|
+
function pageViewWindow(pageNumber) {
|
|
495
|
+
if (pageSettings.rotation.value !== 0) return null;
|
|
496
|
+
return viewWindows.value[pageNumber] ?? null;
|
|
497
|
+
}
|
|
331
498
|
function pageSlotStyle(meta) {
|
|
332
499
|
const s = pageScale.value;
|
|
333
500
|
if (isRotatedSideways.value) {
|
|
@@ -424,26 +591,36 @@ async function loadDocument() {
|
|
|
424
591
|
}
|
|
425
592
|
const source = props.source;
|
|
426
593
|
const baseParams = source instanceof Uint8Array || typeof source === "string" ? { url: typeof source === "string" ? source : void 0, data: source instanceof Uint8Array ? source : void 0 } : { ...source };
|
|
594
|
+
if (baseParams.data instanceof Uint8Array) {
|
|
595
|
+
baseParams.data = baseParams.data.slice();
|
|
596
|
+
} else if (baseParams.data instanceof ArrayBuffer) {
|
|
597
|
+
baseParams.data = baseParams.data.slice(0);
|
|
598
|
+
}
|
|
427
599
|
const docParams = {
|
|
428
600
|
...baseParams,
|
|
429
601
|
standardFontDataUrl: "/_kviewer/standard_fonts/",
|
|
430
602
|
cMapUrl: "/_kviewer/cmaps/",
|
|
431
603
|
cMapPacked: true,
|
|
432
|
-
useSystemFonts: true
|
|
604
|
+
useSystemFonts: true,
|
|
605
|
+
wasmUrl: "/_kviewer/wasm/",
|
|
606
|
+
iccUrl: "/_kviewer/iccs/"
|
|
433
607
|
};
|
|
434
608
|
const loadingTask = pdfjs.getDocument(docParams);
|
|
609
|
+
destroyActiveDoc = () => loadingTask.destroy();
|
|
435
610
|
const pdfDoc = await loadingTask.promise;
|
|
436
611
|
doc.value = pdfDoc;
|
|
437
612
|
viewerState.doc.value = pdfDoc;
|
|
438
613
|
viewerState.totalPages.value = pdfDoc.numPages;
|
|
439
614
|
proxyCache.setDocument(pdfDoc);
|
|
440
615
|
pdfDoc.getData().then(async (bytes) => {
|
|
441
|
-
const [styles, captions] = await Promise.all([
|
|
616
|
+
const [styles, captions, signedSigs] = await Promise.all([
|
|
442
617
|
extractCheckboxStyles(bytes),
|
|
443
|
-
extractButtonCaptions(bytes)
|
|
618
|
+
extractButtonCaptions(bytes),
|
|
619
|
+
extractSignedSignatureNames(bytes)
|
|
444
620
|
]);
|
|
445
621
|
if (styles.size > 0) formFieldsState.applyCheckboxStyles(styles);
|
|
446
622
|
if (captions.size > 0) formFieldsState.applyButtonCaptions(captions);
|
|
623
|
+
if (signedSigs.size > 0) formFieldsState.applySignedSignatures(signedSigs);
|
|
447
624
|
}).catch((err) => console.warn("[KViewer] Failed to read MK data from source PDF:", err));
|
|
448
625
|
if (!scrollContainer.value) {
|
|
449
626
|
await nextTick();
|
|
@@ -794,10 +971,18 @@ onMounted(() => {
|
|
|
794
971
|
{ immediate: true }
|
|
795
972
|
);
|
|
796
973
|
watch(() => viewerState.scale.value, async () => {
|
|
974
|
+
if (suppressScaleReset) {
|
|
975
|
+
suppressScaleReset = false;
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
797
978
|
await nextTick();
|
|
798
979
|
inertiaPanzoom.reset();
|
|
980
|
+
viewWindows.value = {};
|
|
981
|
+
computeViewWindows();
|
|
982
|
+
});
|
|
983
|
+
watch(() => viewerState.currentPage.value, () => {
|
|
984
|
+
if (isSinglePageMode.value) inertiaPanzoom.reset();
|
|
799
985
|
});
|
|
800
|
-
watch(() => viewerState.currentPage.value, () => inertiaPanzoom.reset());
|
|
801
986
|
}
|
|
802
987
|
if (isIPad()) {
|
|
803
988
|
viewerState.setStylusMode(true);
|
|
@@ -818,7 +1003,7 @@ watch(
|
|
|
818
1003
|
searchIndex.destroy();
|
|
819
1004
|
virtualization.destroy();
|
|
820
1005
|
proxyCache.clear();
|
|
821
|
-
|
|
1006
|
+
void destroyActiveDoc?.();
|
|
822
1007
|
loadDocument();
|
|
823
1008
|
}
|
|
824
1009
|
);
|
|
@@ -850,6 +1035,7 @@ watch(
|
|
|
850
1035
|
);
|
|
851
1036
|
onBeforeUnmount(() => {
|
|
852
1037
|
window.removeEventListener("keydown", onGlobalKeydown);
|
|
1038
|
+
clearTimeout(viewWindowTimer);
|
|
853
1039
|
scrollContainer.value?.removeEventListener("wheel", onWheel);
|
|
854
1040
|
inertiaPanzoom.detach();
|
|
855
1041
|
resizeObserver?.disconnect();
|
|
@@ -864,7 +1050,8 @@ onBeforeUnmount(() => {
|
|
|
864
1050
|
virtualization.destroy();
|
|
865
1051
|
proxyCache.clear();
|
|
866
1052
|
painter?.destroy();
|
|
867
|
-
|
|
1053
|
+
void destroyActiveDoc?.();
|
|
1054
|
+
destroyActiveDoc = null;
|
|
868
1055
|
});
|
|
869
1056
|
defineExpose({
|
|
870
1057
|
getAnnotations: () => painter?.getData() ?? [],
|
|
@@ -892,7 +1079,15 @@ defineExpose({
|
|
|
892
1079
|
const next = !viewerState.formEditMode.value;
|
|
893
1080
|
viewerState.setFormEditMode(next);
|
|
894
1081
|
return next;
|
|
895
|
-
}
|
|
1082
|
+
},
|
|
1083
|
+
/** True once the user has viewed every page — each page has entered the
|
|
1084
|
+
* viewport at least once. Use to gate a "must read all pages" sign action.
|
|
1085
|
+
* See also the `all-pages-read` event for a push-style signal. */
|
|
1086
|
+
allPagesRead: () => virtualization.allPagesRead.value,
|
|
1087
|
+
/** Page numbers viewed so far, ascending. */
|
|
1088
|
+
getViewedPages: () => [...virtualization.viewedPages.value].sort((a, b) => a - b),
|
|
1089
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
1090
|
+
resetViewedPages: () => virtualization.resetViewedPages()
|
|
896
1091
|
});
|
|
897
1092
|
</script>
|
|
898
1093
|
|
|
@@ -2,6 +2,9 @@ import { type ViewMode } from '../composables/useViewerState.js';
|
|
|
2
2
|
import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
3
3
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
|
+
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
|
+
import { type KviewerMessageOverrides } from '../composables/useKviewerI18n.js';
|
|
7
|
+
import { type KviewerLocale } from '../i18n/messages.js';
|
|
5
8
|
type __VLS_Props = {
|
|
6
9
|
source: string | Uint8Array | object;
|
|
7
10
|
textLayer?: boolean;
|
|
@@ -45,6 +48,19 @@ type __VLS_Props = {
|
|
|
45
48
|
* toggles or actions without replacing the entire header. Ignored
|
|
46
49
|
* when the host supplies a custom `#header` slot. */
|
|
47
50
|
menuItems?: ViewerMenuItem[];
|
|
51
|
+
/** Configures which toolbar tools render and in what order. Entries are
|
|
52
|
+
* built-in tool IDs (strings) or `{ type: 'slot', ... }` objects that
|
|
53
|
+
* inject a custom button via a `#tool-<name>` slot. When omitted, the
|
|
54
|
+
* default toolbar is rendered unchanged. Ignored when the host supplies
|
|
55
|
+
* a custom `#header` slot. See {@link ViewerToolEntry}. */
|
|
56
|
+
tools?: ViewerToolEntry[];
|
|
57
|
+
/** UI language for built-in viewer strings (toolbar, search, modals,
|
|
58
|
+
* form sidebar). Defaults to the `kviewer.locale` module option, then
|
|
59
|
+
* 'en'. Hosts with their own i18n just bind their active locale. */
|
|
60
|
+
locale?: KviewerLocale;
|
|
61
|
+
/** Per-string overrides for built-in messages — wins over the locale
|
|
62
|
+
* catalog. Useful for app-specific wording without forking locales. */
|
|
63
|
+
messages?: KviewerMessageOverrides;
|
|
48
64
|
};
|
|
49
65
|
declare function exportPdf(options?: ExportPdfOptions): Promise<Uint8Array>;
|
|
50
66
|
type ImportMode = 'replace' | 'merge';
|
|
@@ -55,11 +71,15 @@ declare function importAnnotations(annotations: IAnnotationStore[], options?: {
|
|
|
55
71
|
skipped: number;
|
|
56
72
|
}>;
|
|
57
73
|
declare function getKonvaCanvasState(): Record<number, string>;
|
|
58
|
-
declare var __VLS_1: {},
|
|
74
|
+
declare var __VLS_1: {}, __VLS_11: string, __VLS_12: {
|
|
75
|
+
state: import("../composables/useViewerState.js").ViewerState;
|
|
76
|
+
}, __VLS_62: {};
|
|
59
77
|
type __VLS_Slots = {} & {
|
|
78
|
+
[K in NonNullable<typeof __VLS_11>]?: (props: typeof __VLS_12) => any;
|
|
79
|
+
} & {
|
|
60
80
|
header?: (props: typeof __VLS_1) => any;
|
|
61
81
|
} & {
|
|
62
|
-
footer?: (props: typeof
|
|
82
|
+
footer?: (props: typeof __VLS_62) => any;
|
|
63
83
|
};
|
|
64
84
|
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
65
85
|
getAnnotations: () => IAnnotationStore[];
|
|
@@ -84,12 +104,24 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
84
104
|
setFormEditMode: (enabled: boolean) => void;
|
|
85
105
|
/** Flip form-edit mode. Returns the new state. */
|
|
86
106
|
toggleFormEditMode: () => boolean;
|
|
107
|
+
/** True once the user has viewed every page — each page has entered the
|
|
108
|
+
* viewport at least once. Use to gate a "must read all pages" sign action.
|
|
109
|
+
* See also the `all-pages-read` event for a push-style signal. */
|
|
110
|
+
allPagesRead: () => boolean;
|
|
111
|
+
/** Page numbers viewed so far, ascending. */
|
|
112
|
+
getViewedPages: () => number[];
|
|
113
|
+
/** Clear viewed-page tracking, e.g. to restart a signing session. */
|
|
114
|
+
resetViewedPages: () => void;
|
|
87
115
|
}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
116
|
+
"all-pages-read": () => any;
|
|
88
117
|
"update:formEditMode": (value: boolean) => any;
|
|
89
118
|
"update:activeRoleId": (value: string | null) => any;
|
|
119
|
+
"update:viewedPages": (pages: number[]) => any;
|
|
90
120
|
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
121
|
+
"onAll-pages-read"?: (() => any) | undefined;
|
|
91
122
|
"onUpdate:formEditMode"?: ((value: boolean) => any) | undefined;
|
|
92
123
|
"onUpdate:activeRoleId"?: ((value: string | null) => any) | undefined;
|
|
124
|
+
"onUpdate:viewedPages"?: ((pages: number[]) => any) | undefined;
|
|
93
125
|
}>, {
|
|
94
126
|
userName: string;
|
|
95
127
|
freehandGroupingDelay: number;
|
|
@@ -97,6 +129,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
97
129
|
readonly: boolean;
|
|
98
130
|
active: boolean;
|
|
99
131
|
menuItems: ViewerMenuItem[];
|
|
132
|
+
tools: ViewerToolEntry[];
|
|
100
133
|
scripting: boolean;
|
|
101
134
|
stamps: StampDefinition[];
|
|
102
135
|
signatureHandlers: SignatureHandlers;
|
|
@@ -105,6 +138,8 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
105
138
|
formEditMode: boolean;
|
|
106
139
|
roleColors: Record<string, string>;
|
|
107
140
|
activeRoleId: string | null;
|
|
141
|
+
locale: KviewerLocale;
|
|
142
|
+
messages: KviewerMessageOverrides;
|
|
108
143
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
109
144
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
110
145
|
declare const _default: typeof __VLS_export;
|
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
2
|
+
import { type ViewerToolEntry } from '../toolbar-tools.js';
|
|
2
3
|
type __VLS_Props = {
|
|
3
4
|
/** Extra items appended to the burger menu after the built-ins.
|
|
4
5
|
* See {@link ViewerMenuItem}. Pure forwarding from `<KViewer>`. */
|
|
5
6
|
menuItems?: ViewerMenuItem[];
|
|
7
|
+
/** Configures which toolbar tools render and in what order. When
|
|
8
|
+
* omitted, the default toolbar is rendered. See {@link ViewerToolEntry}. */
|
|
9
|
+
tools?: ViewerToolEntry[];
|
|
6
10
|
};
|
|
7
|
-
declare
|
|
11
|
+
declare var __VLS_109: string, __VLS_110: {
|
|
12
|
+
state: import("../composables/useViewerState.js").ViewerState;
|
|
13
|
+
}, __VLS_154: string, __VLS_155: {
|
|
14
|
+
state: import("../composables/useViewerState.js").ViewerState;
|
|
15
|
+
};
|
|
16
|
+
type __VLS_Slots = {} & {
|
|
17
|
+
[K in NonNullable<typeof __VLS_109>]?: (props: typeof __VLS_110) => any;
|
|
18
|
+
} & {
|
|
19
|
+
[K in NonNullable<typeof __VLS_154>]?: (props: typeof __VLS_155) => any;
|
|
20
|
+
};
|
|
21
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
22
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
8
23
|
declare const _default: typeof __VLS_export;
|
|
9
24
|
export default _default;
|
|
25
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
26
|
+
new (): {
|
|
27
|
+
$slots: S;
|
|
28
|
+
};
|
|
29
|
+
};
|