kviewer 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +15 -15
- package/dist/runtime/annotation/engine/editor/selector.d.ts +1 -0
- package/dist/runtime/annotation/engine/editor/selector.js +17 -5
- package/dist/runtime/annotation/engine/painter.d.ts +2 -0
- package/dist/runtime/annotation/engine/painter.js +4 -0
- package/dist/runtime/annotation/pdf-export/export-form-fields.js +26 -41
- package/dist/runtime/annotation/pdf-export/print.d.ts +12 -0
- package/dist/runtime/annotation/pdf-export/print.js +63 -0
- package/dist/runtime/assets/kviewer.css +1 -1
- package/dist/runtime/components/AnnotationToolbar.vue +12 -1
- package/dist/runtime/components/FormFieldLayer.vue +7 -2
- package/dist/runtime/components/PdfPage.vue +24 -1
- package/dist/runtime/components/SelectionToolbarItems.d.vue.ts +8 -0
- package/dist/runtime/components/SelectionToolbarItems.vue +43 -0
- package/dist/runtime/components/SelectionToolbarItems.vue.d.ts +8 -0
- package/dist/runtime/components/Viewer.d.vue.ts +17 -1
- package/dist/runtime/components/Viewer.vue +45 -2
- package/dist/runtime/components/Viewer.vue.d.ts +17 -1
- package/dist/runtime/components/ViewerBar.d.vue.ts +4 -4
- package/dist/runtime/components/ViewerBar.vue +2 -1
- package/dist/runtime/components/ViewerBar.vue.d.ts +4 -4
- package/dist/runtime/components/ViewerTabs.d.vue.ts +4 -0
- package/dist/runtime/components/ViewerTabs.vue +2 -0
- package/dist/runtime/components/ViewerTabs.vue.d.ts +4 -0
- package/dist/runtime/components/form-fields/FormFieldWrapper.vue +15 -2
- package/dist/runtime/components/form-fields/PlacedFieldChrome.vue +9 -9
- package/dist/runtime/components/form-fields/PlacedFieldToolbar.d.vue.ts +7 -0
- package/dist/runtime/components/form-fields/PlacedFieldToolbar.vue +51 -0
- package/dist/runtime/components/form-fields/PlacedFieldToolbar.vue.d.ts +7 -0
- package/dist/runtime/components/tools/ToolbarMenu.vue +17 -0
- package/dist/runtime/composables/useScriptingManager.d.ts +5 -0
- package/dist/runtime/composables/useScriptingManager.js +6 -0
- package/dist/runtime/composables/useViewerState.d.ts +2 -0
- package/dist/runtime/composables/useViewerState.js +8 -0
- package/dist/runtime/embed/bridge-client.d.ts +4 -0
- package/dist/runtime/embed/bridge-client.js +6 -0
- package/dist/runtime/embed/bridge-host.d.ts +10 -1
- package/dist/runtime/embed/bridge-host.js +102 -28
- package/dist/runtime/embed/protocol.d.ts +17 -0
- package/dist/runtime/embed/protocol.js +1 -0
- package/dist/runtime/i18n/messages.d.ts +2 -0
- package/dist/runtime/i18n/messages.js +2 -0
- package/dist/runtime/public-types.d.ts +4 -3
- package/dist/runtime/public-types.js +4 -1
- package/dist/runtime/selection-items.d.ts +61 -0
- package/dist/runtime/selection-items.js +1 -0
- package/dist/runtime/toolbar-tools.d.ts +4 -1
- package/dist/types.d.mts +1 -1
- package/package.json +1 -1
|
@@ -193,7 +193,8 @@ import {
|
|
|
193
193
|
triggerRef,
|
|
194
194
|
onMounted,
|
|
195
195
|
onBeforeUnmount,
|
|
196
|
-
useSlots
|
|
196
|
+
useSlots,
|
|
197
|
+
provide
|
|
197
198
|
} from "vue";
|
|
198
199
|
import { useRuntimeConfig } from "#imports";
|
|
199
200
|
import ViewerBar from "./ViewerBar.vue";
|
|
@@ -214,6 +215,7 @@ import {
|
|
|
214
215
|
} from "../annotation/pdf-import/decode";
|
|
215
216
|
import { getTimestampString } from "../annotation/engine/utils";
|
|
216
217
|
import { downloadPdfBytes } from "../annotation/pdf-export/download";
|
|
218
|
+
import { printPdfBytes } from "../annotation/pdf-export/print";
|
|
217
219
|
import { exportAnnotationsToPdf } from "../annotation/pdf-export/export";
|
|
218
220
|
import { createPageVirtualization } from "../composables/usePageVirtualization";
|
|
219
221
|
import { createPageProxyCache } from "../composables/usePageProxyCache";
|
|
@@ -225,6 +227,7 @@ import { isIPad } from "../annotation/engine/input-device";
|
|
|
225
227
|
import { useInertiaPanzoom } from "../composables/useInertiaPanzoom";
|
|
226
228
|
import { createScriptingManager } from "../composables/useScriptingManager";
|
|
227
229
|
import { createScriptingBridge } from "../composables/useScriptingBridge";
|
|
230
|
+
import { SELECTION_ITEMS_KEY } from "../selection-items";
|
|
228
231
|
import { provideKviewerI18n } from "../composables/useKviewerI18n";
|
|
229
232
|
import { KVIEWER_LOCALES } from "../i18n/messages";
|
|
230
233
|
const props = defineProps({
|
|
@@ -247,10 +250,12 @@ const props = defineProps({
|
|
|
247
250
|
fieldDefaults: { type: Object, required: false, default: void 0 },
|
|
248
251
|
scripting: { type: Boolean, required: false, default: false },
|
|
249
252
|
menuItems: { type: Array, required: false, default: void 0 },
|
|
253
|
+
selectionItems: { type: Array, required: false, default: void 0 },
|
|
250
254
|
tools: { type: Array, required: false, default: void 0 },
|
|
251
255
|
locale: { type: null, required: false, default: void 0 },
|
|
252
256
|
messages: { type: Object, required: false, default: void 0 }
|
|
253
257
|
});
|
|
258
|
+
provide(SELECTION_ITEMS_KEY, computed(() => props.selectionItems));
|
|
254
259
|
const slots = useSlots();
|
|
255
260
|
const toolSlotNames = computed(
|
|
256
261
|
() => Object.keys(slots).filter((name) => name.startsWith("tool-"))
|
|
@@ -260,7 +265,7 @@ const fieldPlacedCallbacks = /* @__PURE__ */ new Set();
|
|
|
260
265
|
const signedStatusCallbacks = /* @__PURE__ */ new Set();
|
|
261
266
|
const viewerRoot = ref(null);
|
|
262
267
|
const scrollContainer = ref(null);
|
|
263
|
-
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn, setApplyScaleFn } = provideViewerState();
|
|
268
|
+
const { state: viewerState, setScrollToPageFn, setDownloadPdfFn, setPrintPdfFn, setApplyScaleFn } = provideViewerState();
|
|
264
269
|
watchEffect(() => {
|
|
265
270
|
viewerState.readonly.value = props.readonly ?? false;
|
|
266
271
|
});
|
|
@@ -315,6 +320,25 @@ watch(
|
|
|
315
320
|
},
|
|
316
321
|
{ immediate: true }
|
|
317
322
|
);
|
|
323
|
+
watch(
|
|
324
|
+
() => formFieldsState.selectedPlacedFieldId.value,
|
|
325
|
+
(id) => {
|
|
326
|
+
if (!id) return;
|
|
327
|
+
viewerState.painter.value?.clearSelection();
|
|
328
|
+
viewerState.selectedAnnotation.value = null;
|
|
329
|
+
viewerState.selectedAnnotations.value = [];
|
|
330
|
+
viewerState.selectionRect.value = null;
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
watch(
|
|
334
|
+
[
|
|
335
|
+
() => viewerState.selectedAnnotation.value,
|
|
336
|
+
() => viewerState.selectedAnnotations.value
|
|
337
|
+
],
|
|
338
|
+
([single, multi]) => {
|
|
339
|
+
if (single || multi.length > 0) formFieldsState.selectPlacedField(null);
|
|
340
|
+
}
|
|
341
|
+
);
|
|
318
342
|
const signatureStatus = computed(() => formFieldsState.getSignatureFieldStatus());
|
|
319
343
|
const signatureStatusKey = (fields) => fields.map((s) => `${s.id}|${s.fieldName}|${s.signed ? 1 : 0}`).join(";");
|
|
320
344
|
watch(signatureStatus, (next, prev) => {
|
|
@@ -806,6 +830,18 @@ async function exportPdf(options = {}) {
|
|
|
806
830
|
}
|
|
807
831
|
return bytes;
|
|
808
832
|
}
|
|
833
|
+
async function printPdf(options = {}) {
|
|
834
|
+
if (scriptingManager?.isReady()) {
|
|
835
|
+
await scriptingManager.dispatchWillPrint().catch(() => {
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
const bytes = await exportPdf({ ...options, download: false });
|
|
839
|
+
await printPdfBytes(bytes);
|
|
840
|
+
if (scriptingManager?.isReady()) {
|
|
841
|
+
scriptingManager.dispatchDidPrint().catch(() => {
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
809
845
|
async function importAnnotations(annotations, options) {
|
|
810
846
|
const mode = options?.mode ?? "replace";
|
|
811
847
|
const normalized = normalizeImportedAnnotations(
|
|
@@ -1025,6 +1061,9 @@ onMounted(() => {
|
|
|
1025
1061
|
setDownloadPdfFn(async () => {
|
|
1026
1062
|
await exportPdf({ download: true });
|
|
1027
1063
|
});
|
|
1064
|
+
setPrintPdfFn(async () => {
|
|
1065
|
+
await printPdf();
|
|
1066
|
+
});
|
|
1028
1067
|
loadDocument();
|
|
1029
1068
|
window.addEventListener("keydown", onGlobalKeydown);
|
|
1030
1069
|
});
|
|
@@ -1094,6 +1133,10 @@ defineExpose({
|
|
|
1094
1133
|
getAnnotations: () => painter?.getData() ?? [],
|
|
1095
1134
|
importAnnotations,
|
|
1096
1135
|
exportPdf,
|
|
1136
|
+
/** Export the document (same options as `exportPdf`, minus `download`)
|
|
1137
|
+
* and open the browser's print dialog on the resulting vector PDF —
|
|
1138
|
+
* full print quality, unlike printing the screen canvases. */
|
|
1139
|
+
printPdf,
|
|
1097
1140
|
getKonvaCanvasState,
|
|
1098
1141
|
getFormFieldValues: () => formFieldsState.getAllFieldValues(),
|
|
1099
1142
|
setFormFieldValue: (fieldName, value) => formFieldsState.setFieldValueByName(fieldName, value),
|
|
@@ -3,6 +3,7 @@ import type { AddFormFieldPayload, FormFieldDefinition, IAnnotationStore, Placed
|
|
|
3
3
|
import { type ExportPdfOptions } from '../annotation/pdf-export/export.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
5
|
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
|
+
import { type ViewerSelectionItem } from '../selection-items.js';
|
|
6
7
|
import { type KviewerMessageOverrides } from '../composables/useKviewerI18n.js';
|
|
7
8
|
import { type KviewerLocale } from '../i18n/messages.js';
|
|
8
9
|
type __VLS_Props = {
|
|
@@ -63,6 +64,11 @@ type __VLS_Props = {
|
|
|
63
64
|
* toggles or actions without replacing the entire header. Ignored
|
|
64
65
|
* when the host supplies a custom `#header` slot. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Custom controls appended to the floating selection popover — shown
|
|
68
|
+
* when an annotation or a placed form field (e.g. a signature field)
|
|
69
|
+
* is selected. Buttons and selects; every callback receives the
|
|
70
|
+
* current selection. See {@link ViewerSelectionItem}. */
|
|
71
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
72
|
/** Configures which toolbar tools render and in what order. Entries are
|
|
67
73
|
* built-in tool IDs (strings) or `{ type: 'slot', ... }` objects that
|
|
68
74
|
* inject a custom button via a `#tool-<name>` slot. When omitted, the
|
|
@@ -78,6 +84,11 @@ type __VLS_Props = {
|
|
|
78
84
|
messages?: KviewerMessageOverrides;
|
|
79
85
|
};
|
|
80
86
|
declare function exportPdf(options?: ExportPdfOptions): Promise<Uint8Array>;
|
|
87
|
+
/** Print via the real PDF instead of the on-screen canvases: export the
|
|
88
|
+
* document (annotations + form values baked in) and hand the vector bytes
|
|
89
|
+
* to the browser's native PDF print pipeline. Screen canvases are sized
|
|
90
|
+
* for the monitor and would come out blurry on paper. */
|
|
91
|
+
declare function printPdf(options?: ExportPdfOptions): Promise<void>;
|
|
81
92
|
type ImportMode = 'replace' | 'merge';
|
|
82
93
|
declare function importAnnotations(annotations: IAnnotationStore[], options?: {
|
|
83
94
|
mode?: ImportMode;
|
|
@@ -100,6 +111,10 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
100
111
|
getAnnotations: () => IAnnotationStore[];
|
|
101
112
|
importAnnotations: typeof importAnnotations;
|
|
102
113
|
exportPdf: typeof exportPdf;
|
|
114
|
+
/** Export the document (same options as `exportPdf`, minus `download`)
|
|
115
|
+
* and open the browser's print dialog on the resulting vector PDF —
|
|
116
|
+
* full print quality, unlike printing the screen canvases. */
|
|
117
|
+
printPdf: typeof printPdf;
|
|
103
118
|
getKonvaCanvasState: typeof getKonvaCanvasState;
|
|
104
119
|
getFormFieldValues: () => import("../annotation/engine/types.js").FormFieldValue[];
|
|
105
120
|
setFormFieldValue: (fieldName: string, value: string | boolean | string[]) => void;
|
|
@@ -180,6 +195,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
180
195
|
clickToSign: boolean;
|
|
181
196
|
readonly: boolean;
|
|
182
197
|
active: boolean;
|
|
198
|
+
formEditMode: boolean;
|
|
183
199
|
locale: KviewerLocale;
|
|
184
200
|
menuItems: ViewerMenuItem[];
|
|
185
201
|
tools: ViewerToolEntry[];
|
|
@@ -188,11 +204,11 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
188
204
|
signatureHandlers: SignatureHandlers;
|
|
189
205
|
viewMode: ViewMode;
|
|
190
206
|
shapeDetection: boolean;
|
|
191
|
-
formEditMode: boolean;
|
|
192
207
|
roleColors: Record<string, string>;
|
|
193
208
|
activeRoleId: string | null;
|
|
194
209
|
editablePlacedFields: boolean;
|
|
195
210
|
fieldDefaults: PlacedFieldDefaults;
|
|
211
|
+
selectionItems: ViewerSelectionItem[];
|
|
196
212
|
messages: KviewerMessageOverrides;
|
|
197
213
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
198
214
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
* omitted, the default toolbar is rendered. See {@link ViewerToolEntry}. */
|
|
9
9
|
tools?: ViewerToolEntry[];
|
|
10
10
|
};
|
|
11
|
-
declare var
|
|
11
|
+
declare var __VLS_104: string, __VLS_105: {
|
|
12
12
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
13
|
-
},
|
|
13
|
+
}, __VLS_149: string, __VLS_150: {
|
|
14
14
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
15
15
|
};
|
|
16
16
|
type __VLS_Slots = {} & {
|
|
17
|
-
[K in NonNullable<typeof
|
|
17
|
+
[K in NonNullable<typeof __VLS_104>]?: (props: typeof __VLS_105) => any;
|
|
18
18
|
} & {
|
|
19
|
-
[K in NonNullable<typeof
|
|
19
|
+
[K in NonNullable<typeof __VLS_149>]?: (props: typeof __VLS_150) => any;
|
|
20
20
|
};
|
|
21
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
22
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
|
|
12
12
|
<ZoomControls />
|
|
13
13
|
|
|
14
|
+
<!-- No MarqueeTool button: hand mode covers mouse rubber-band
|
|
15
|
+
selection; `marquee` stays available via the `tools` prop. -->
|
|
14
16
|
<template v-if="!state.readonly.value">
|
|
15
17
|
<USeparator orientation="vertical" class="h-5" :ui="{ border: 'border-accented' }" />
|
|
16
18
|
|
|
17
19
|
<HandTool />
|
|
18
|
-
<MarqueeTool />
|
|
19
20
|
</template>
|
|
20
21
|
|
|
21
22
|
<div class="ml-auto flex items-center gap-2">
|
|
@@ -8,15 +8,15 @@ type __VLS_Props = {
|
|
|
8
8
|
* omitted, the default toolbar is rendered. See {@link ViewerToolEntry}. */
|
|
9
9
|
tools?: ViewerToolEntry[];
|
|
10
10
|
};
|
|
11
|
-
declare var
|
|
11
|
+
declare var __VLS_104: string, __VLS_105: {
|
|
12
12
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
13
|
-
},
|
|
13
|
+
}, __VLS_149: string, __VLS_150: {
|
|
14
14
|
state: import("../composables/useViewerState.js").ViewerState;
|
|
15
15
|
};
|
|
16
16
|
type __VLS_Slots = {} & {
|
|
17
|
-
[K in NonNullable<typeof
|
|
17
|
+
[K in NonNullable<typeof __VLS_104>]?: (props: typeof __VLS_105) => any;
|
|
18
18
|
} & {
|
|
19
|
-
[K in NonNullable<typeof
|
|
19
|
+
[K in NonNullable<typeof __VLS_149>]?: (props: typeof __VLS_150) => any;
|
|
20
20
|
};
|
|
21
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
22
|
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
@@ -2,6 +2,7 @@ import { type ComponentPublicInstance } from 'vue';
|
|
|
2
2
|
import type { ViewMode } from '../composables/useViewerState.js';
|
|
3
3
|
import type { FormFieldDefinition, PlacedFieldDefaults, SignatureFieldStatus, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
|
+
import type { ViewerSelectionItem } from '../selection-items.js';
|
|
5
6
|
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
7
|
export interface ViewerTabItem {
|
|
7
8
|
/** Unique identifier for the tab. */
|
|
@@ -63,6 +64,8 @@ type __VLS_Props = {
|
|
|
63
64
|
scripting?: boolean;
|
|
64
65
|
/** Forwarded to every Viewer instance — see KViewer's `menuItems`. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Forwarded to every Viewer instance — see KViewer's `selectionItems`. */
|
|
68
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
69
|
/** Forwarded to every Viewer instance — see KViewer's `tools`. */
|
|
67
70
|
tools?: ViewerToolEntry[];
|
|
68
71
|
/** Forwarded to every Viewer instance — see KViewer's `clickToSign`. */
|
|
@@ -436,6 +439,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
436
439
|
activeRoleId: string | null;
|
|
437
440
|
editablePlacedFields: boolean;
|
|
438
441
|
fieldDefaults: PlacedFieldDefaults;
|
|
442
|
+
selectionItems: ViewerSelectionItem[];
|
|
439
443
|
defaultActiveTab: string;
|
|
440
444
|
minTabs: number;
|
|
441
445
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
:shape-detection="activeTab.shapeDetection ?? shapeDetection"
|
|
62
62
|
:scripting="activeTab.scripting ?? scripting"
|
|
63
63
|
:menu-items="menuItems"
|
|
64
|
+
:selection-items="selectionItems"
|
|
64
65
|
:tools="tools"
|
|
65
66
|
:role-colors="roleColors"
|
|
66
67
|
:active-role-id="activeRoleId"
|
|
@@ -112,6 +113,7 @@ const props = defineProps({
|
|
|
112
113
|
activeRoleId: { type: [String, null], required: false, default: void 0 },
|
|
113
114
|
scripting: { type: Boolean, required: false, default: false },
|
|
114
115
|
menuItems: { type: Array, required: false, default: void 0 },
|
|
116
|
+
selectionItems: { type: Array, required: false, default: void 0 },
|
|
115
117
|
tools: { type: Array, required: false, default: void 0 },
|
|
116
118
|
clickToSign: { type: Boolean, required: false, default: false },
|
|
117
119
|
editablePlacedFields: { type: Boolean, required: false, default: false },
|
|
@@ -2,6 +2,7 @@ import { type ComponentPublicInstance } from 'vue';
|
|
|
2
2
|
import type { ViewMode } from '../composables/useViewerState.js';
|
|
3
3
|
import type { FormFieldDefinition, PlacedFieldDefaults, SignatureFieldStatus, StampDefinition, SignatureHandlers } from '../annotation/engine/types.js';
|
|
4
4
|
import type { ViewerMenuItem } from '../menu-items.js';
|
|
5
|
+
import type { ViewerSelectionItem } from '../selection-items.js';
|
|
5
6
|
import type { ViewerToolEntry } from '../toolbar-tools.js';
|
|
6
7
|
export interface ViewerTabItem {
|
|
7
8
|
/** Unique identifier for the tab. */
|
|
@@ -63,6 +64,8 @@ type __VLS_Props = {
|
|
|
63
64
|
scripting?: boolean;
|
|
64
65
|
/** Forwarded to every Viewer instance — see KViewer's `menuItems`. */
|
|
65
66
|
menuItems?: ViewerMenuItem[];
|
|
67
|
+
/** Forwarded to every Viewer instance — see KViewer's `selectionItems`. */
|
|
68
|
+
selectionItems?: ViewerSelectionItem[];
|
|
66
69
|
/** Forwarded to every Viewer instance — see KViewer's `tools`. */
|
|
67
70
|
tools?: ViewerToolEntry[];
|
|
68
71
|
/** Forwarded to every Viewer instance — see KViewer's `clickToSign`. */
|
|
@@ -436,6 +439,7 @@ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {
|
|
|
436
439
|
activeRoleId: string | null;
|
|
437
440
|
editablePlacedFields: boolean;
|
|
438
441
|
fieldDefaults: PlacedFieldDefaults;
|
|
442
|
+
selectionItems: ViewerSelectionItem[];
|
|
439
443
|
defaultActiveTab: string;
|
|
440
444
|
minTabs: number;
|
|
441
445
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
'kviewer-form-field--placed': isPlaced,
|
|
7
7
|
'kviewer-form-field--selected': props.selected
|
|
8
8
|
}"
|
|
9
|
-
:style="[positionStyle, {
|
|
9
|
+
:style="[positionStyle, {
|
|
10
|
+
'--kvw-field-color': chromeColor,
|
|
11
|
+
'--kvw-handle-zoom': handleZoom
|
|
12
|
+
}]"
|
|
10
13
|
:data-element-id="props.field.id"
|
|
11
14
|
>
|
|
12
15
|
<div
|
|
@@ -29,6 +32,11 @@
|
|
|
29
32
|
:page-height="props.pageTransform.height"
|
|
30
33
|
@select="emit('select')"
|
|
31
34
|
/>
|
|
35
|
+
|
|
36
|
+
<PlacedFieldToolbar
|
|
37
|
+
v-if="showChrome && props.selected && !state.readonly.value"
|
|
38
|
+
:field="props.field"
|
|
39
|
+
/>
|
|
32
40
|
</div>
|
|
33
41
|
</template>
|
|
34
42
|
|
|
@@ -44,6 +52,7 @@ import FormDropdown from "./FormDropdown.vue";
|
|
|
44
52
|
import FormSignatureField from "./FormSignatureField.vue";
|
|
45
53
|
import FormButton from "./FormButton.vue";
|
|
46
54
|
import PlacedFieldChrome from "./PlacedFieldChrome.vue";
|
|
55
|
+
import PlacedFieldToolbar from "./PlacedFieldToolbar.vue";
|
|
47
56
|
const props = defineProps({
|
|
48
57
|
field: { type: Object, required: true },
|
|
49
58
|
pageTransform: { type: Object, required: true },
|
|
@@ -58,6 +67,10 @@ const isFormEditMode = computed(() => state.formEditMode.value);
|
|
|
58
67
|
const showChrome = computed(
|
|
59
68
|
() => isFormEditMode.value || formFields.editablePlacedFields.value && isPlaced.value
|
|
60
69
|
);
|
|
70
|
+
const handleZoom = computed(() => {
|
|
71
|
+
const totalZoom = (props.scale || 1) * (state.visualZoom.value || 1);
|
|
72
|
+
return (1 / totalZoom).toFixed(3);
|
|
73
|
+
});
|
|
61
74
|
const chromeColor = computed(() => {
|
|
62
75
|
const id = props.field.roleId;
|
|
63
76
|
if (!id) return "#1677ff";
|
|
@@ -77,5 +90,5 @@ const positionStyle = computed(() => {
|
|
|
77
90
|
</script>
|
|
78
91
|
|
|
79
92
|
<style>
|
|
80
|
-
.kviewer-form-field__body{height:100%;width:100%}.kviewer-form-field__body--locked{pointer-events:none}
|
|
93
|
+
.kviewer-form-field__body{height:100%;width:100%}.kviewer-form-field__body--locked{pointer-events:none}
|
|
81
94
|
</style>
|
|
@@ -6,10 +6,16 @@
|
|
|
6
6
|
ref="rootEl"
|
|
7
7
|
class="kviewer-placed-chrome"
|
|
8
8
|
:class="{ 'kviewer-placed-chrome--selected': selected }"
|
|
9
|
-
:style="{ '--kvw-handle-zoom': handleZoom }"
|
|
10
9
|
@pointerdown.stop="onChromePointerDown"
|
|
11
10
|
@click.stop
|
|
12
11
|
>
|
|
12
|
+
<div
|
|
13
|
+
class="kviewer-placed-chrome__outline"
|
|
14
|
+
:class="{
|
|
15
|
+
'kviewer-placed-chrome__outline--placed': field.origin === 'placed',
|
|
16
|
+
'kviewer-placed-chrome__outline--selected': selected
|
|
17
|
+
}"
|
|
18
|
+
/>
|
|
13
19
|
<div
|
|
14
20
|
v-if="selected"
|
|
15
21
|
class="kviewer-placed-chrome__grip"
|
|
@@ -33,15 +39,9 @@
|
|
|
33
39
|
</template>
|
|
34
40
|
|
|
35
41
|
<script setup>
|
|
36
|
-
import {
|
|
42
|
+
import { ref } from "vue";
|
|
37
43
|
import { useFormFields } from "../../composables/useFormFields";
|
|
38
|
-
import { useViewerState } from "../../composables/useViewerState";
|
|
39
44
|
const rootEl = ref(null);
|
|
40
|
-
const viewerState = useViewerState();
|
|
41
|
-
const handleZoom = computed(() => {
|
|
42
|
-
const totalZoom = (props.scale || 1) * (viewerState.visualZoom.value || 1);
|
|
43
|
-
return Math.max(0.4, 1 / totalZoom).toFixed(3);
|
|
44
|
-
});
|
|
45
45
|
const props = defineProps({
|
|
46
46
|
field: { type: Object, required: true },
|
|
47
47
|
selected: { type: Boolean, required: true },
|
|
@@ -127,5 +127,5 @@ function onHandlePointerDown(e, name) {
|
|
|
127
127
|
</script>
|
|
128
128
|
|
|
129
129
|
<style>
|
|
130
|
-
.kviewer-placed-chrome{cursor:move;inset:0;pointer-events:auto;position:absolute;touch-action:none}.kviewer-placed-
|
|
130
|
+
.kviewer-placed-chrome{cursor:move;inset:0;pointer-events:auto;position:absolute;touch-action:none}.kviewer-placed-chrome__outline{border:1px dashed color-mix(in srgb,var(--kvw-field-color,#1677ff) 45%,transparent);box-sizing:border-box;height:calc(100%/var(--kvw-handle-zoom, 1));left:0;pointer-events:none;position:absolute;top:0;transform:scale(var(--kvw-handle-zoom,1));transform-origin:0 0;width:calc(100%/var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__outline--placed{border-color:color-mix(in srgb,var(--kvw-field-color,#1677ff) 75%,transparent)}.kviewer-placed-chrome__outline--selected{border:1.5px dashed var(--kvw-field-color,#1677ff)}.kviewer-placed-chrome__grip{align-items:center;background:color-mix(in srgb,var(--kvw-field-color,#1677ff) 15%,transparent);border:1px solid color-mix(in srgb,var(--kvw-field-color,#1677ff) 40%,transparent);border-radius:4px;bottom:calc(100% + 4px*var(--kvw-handle-zoom, 1));box-sizing:border-box;color:var(--kvw-field-color,#1677ff);cursor:move;display:flex;font-size:12px;gap:4px;height:22px;left:0;line-height:1;overflow:hidden;padding:0 8px 0 3px;pointer-events:auto;position:absolute;touch-action:none;transform:scale(var(--kvw-handle-zoom,1));transform-origin:0 100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:calc(100%/var(--kvw-handle-zoom, 1))}.kviewer-placed-chrome__grip-dots{color:var(--kvw-field-color,#1677ff);flex:0 0 auto;height:13px;width:13px}.kviewer-placed-chrome__grip-label{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kviewer-placed-chrome__handle{background:#fff;border:1px solid var(--kvw-field-color,#1677ff);border-radius:2px;height:8px;pointer-events:auto;position:absolute;touch-action:none;transform:scale(var(--kvw-handle-zoom,1));width:8px}.kviewer-placed-chrome__handle:before{content:"";inset:-8px;position:absolute}.kviewer-placed-chrome__handle--n{left:50%;top:-4px;transform:translateX(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--s{bottom:-4px;left:50%;transform:translateX(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--e{right:-4px;top:50%;transform:translateY(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--w{left:-4px;top:50%;transform:translateY(-50%) scale(var(--kvw-handle-zoom,1))}.kviewer-placed-chrome__handle--ne{right:-4px;top:-4px}.kviewer-placed-chrome__handle--nw{left:-4px;top:-4px}.kviewer-placed-chrome__handle--se{bottom:-4px;right:-4px}.kviewer-placed-chrome__handle--sw{bottom:-4px;left:-4px}
|
|
131
131
|
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FormFieldDefinition } from '../../annotation/engine/types.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
field: FormFieldDefinition;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: 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>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Same pill as AnnotationToolbar, but anchored in the page DOM below
|
|
3
|
+
the selected field so it follows pan/zoom for free (the Konva
|
|
4
|
+
selection popover is viewport-fixed because the stage has no DOM
|
|
5
|
+
anchor to attach to). Stops pointer events so a click here never
|
|
6
|
+
reaches page-level deselect handlers. -->
|
|
7
|
+
<div
|
|
8
|
+
class="kviewer-placed-toolbar"
|
|
9
|
+
@pointerdown.stop
|
|
10
|
+
@click.stop
|
|
11
|
+
>
|
|
12
|
+
<div class="flex items-center gap-0.5 bg-elevated rounded-lg shadow-lg border border-default p-1">
|
|
13
|
+
<SelectionToolbarItems :items="customItems" :ctx="selectionCtx" />
|
|
14
|
+
|
|
15
|
+
<button
|
|
16
|
+
class="flex items-center justify-center w-7 h-7 rounded-md hover:bg-default cursor-pointer"
|
|
17
|
+
:title="t('deleteField')"
|
|
18
|
+
data-testid="placed-field-delete"
|
|
19
|
+
@click="onDelete"
|
|
20
|
+
>
|
|
21
|
+
<UIcon name="i-lucide-trash-2" class="text-sm text-error" />
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup>
|
|
28
|
+
import { computed, inject } from "vue";
|
|
29
|
+
import { useFormFields } from "../../composables/useFormFields";
|
|
30
|
+
import { useKviewerI18n } from "../../composables/useKviewerI18n";
|
|
31
|
+
import { SELECTION_ITEMS_KEY } from "../../selection-items";
|
|
32
|
+
import SelectionToolbarItems from "../SelectionToolbarItems.vue";
|
|
33
|
+
const props = defineProps({
|
|
34
|
+
field: { type: Object, required: true }
|
|
35
|
+
});
|
|
36
|
+
const formFields = useFormFields();
|
|
37
|
+
const { t } = useKviewerI18n();
|
|
38
|
+
const customItems = inject(SELECTION_ITEMS_KEY, void 0);
|
|
39
|
+
const selectionCtx = computed(() => ({
|
|
40
|
+
kind: "field",
|
|
41
|
+
annotations: [],
|
|
42
|
+
field: props.field
|
|
43
|
+
}));
|
|
44
|
+
function onDelete() {
|
|
45
|
+
formFields.removePlacedField(props.field.id);
|
|
46
|
+
}
|
|
47
|
+
</script>
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.kviewer-placed-toolbar{left:50%;pointer-events:auto;position:absolute;top:calc(100% + 8px*var(--kvw-handle-zoom, 1));transform:translateX(-50%) scale(var(--kvw-handle-zoom,1));transform-origin:50% 0;width:-moz-max-content;width:max-content;z-index:1}
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FormFieldDefinition } from '../../annotation/engine/types.js';
|
|
2
|
+
type __VLS_Props = {
|
|
3
|
+
field: FormFieldDefinition;
|
|
4
|
+
};
|
|
5
|
+
declare const __VLS_export: 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>;
|
|
6
|
+
declare const _default: typeof __VLS_export;
|
|
7
|
+
export default _default;
|
|
@@ -13,6 +13,16 @@
|
|
|
13
13
|
data-testid="viewer-download"
|
|
14
14
|
@click="onDownload"
|
|
15
15
|
/>
|
|
16
|
+
<UButton
|
|
17
|
+
icon="i-lucide-printer"
|
|
18
|
+
:label="t('print')"
|
|
19
|
+
variant="ghost"
|
|
20
|
+
color="neutral"
|
|
21
|
+
size="xs"
|
|
22
|
+
class="w-full justify-start"
|
|
23
|
+
data-testid="viewer-print"
|
|
24
|
+
@click="onPrint"
|
|
25
|
+
/>
|
|
16
26
|
<UButton
|
|
17
27
|
:icon="state.shapeDetection.value ? 'i-lucide-square-check-big' : 'i-lucide-square-dashed'"
|
|
18
28
|
:label="state.shapeDetection.value ? t('disableFormFieldDetection') : t('detectFormFields')"
|
|
@@ -76,4 +86,11 @@ async function onDownload() {
|
|
|
76
86
|
console.error("Failed to export PDF from menu action", error);
|
|
77
87
|
}
|
|
78
88
|
}
|
|
89
|
+
async function onPrint() {
|
|
90
|
+
try {
|
|
91
|
+
await state.printPdf();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error("Failed to print PDF from menu action", error);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
79
96
|
</script>
|
|
@@ -44,6 +44,11 @@ export interface ScriptingManager {
|
|
|
44
44
|
* PageOpen action can fire (the manager's `updatefromsandbox` /
|
|
45
45
|
* pagerendered handling in pdf_viewer.mjs). */
|
|
46
46
|
notifyPageRendered: (pageNumber: number) => void;
|
|
47
|
+
/** Run the document's `WillPrint` action and wait for the sandbox to
|
|
48
|
+
* finish it (scripts may adjust field values before printing). */
|
|
49
|
+
dispatchWillPrint: () => Promise<void>;
|
|
50
|
+
/** Fire the document's `DidPrint` action. */
|
|
51
|
+
dispatchDidPrint: () => Promise<void>;
|
|
47
52
|
/** Bind a document to the sandbox. Safe to call repeatedly — pass
|
|
48
53
|
* null to detach (e.g. before swapping documents). */
|
|
49
54
|
setDocument: (pdfDoc: PDFDocumentProxy | null) => Promise<void>;
|
|
@@ -104,6 +104,12 @@ export async function createScriptingManager(deps) {
|
|
|
104
104
|
notifyPageRendered(pageNumber) {
|
|
105
105
|
eventBus.dispatch("pagerendered", { source: window, pageNumber });
|
|
106
106
|
},
|
|
107
|
+
async dispatchWillPrint() {
|
|
108
|
+
await manager.dispatchWillPrint();
|
|
109
|
+
},
|
|
110
|
+
async dispatchDidPrint() {
|
|
111
|
+
await manager.dispatchDidPrint();
|
|
112
|
+
},
|
|
107
113
|
async setDocument(pdfDoc) {
|
|
108
114
|
await manager.setDocument(pdfDoc);
|
|
109
115
|
},
|
|
@@ -54,6 +54,7 @@ export interface ViewerState {
|
|
|
54
54
|
setStylusMode: (enabled: boolean) => void;
|
|
55
55
|
scrollToPage: (pageNumber: number) => void;
|
|
56
56
|
downloadPdf: () => Promise<void>;
|
|
57
|
+
printPdf: () => Promise<void>;
|
|
57
58
|
history: AnnotationHistory;
|
|
58
59
|
readonly: Ref<boolean>;
|
|
59
60
|
shapeDetection: Ref<boolean>;
|
|
@@ -66,6 +67,7 @@ export interface ViewerStateProvider {
|
|
|
66
67
|
state: ViewerState;
|
|
67
68
|
setScrollToPageFn: (fn: (pageNumber: number) => void) => void;
|
|
68
69
|
setDownloadPdfFn: (fn: () => Promise<void>) => void;
|
|
70
|
+
setPrintPdfFn: (fn: () => Promise<void>) => void;
|
|
69
71
|
/** Register how custom-scale changes are applied. The Viewer registers a
|
|
70
72
|
* position-preserving commit (zoom about the viewport centre); without
|
|
71
73
|
* one, setScale falls back to a plain scale write. */
|
|
@@ -152,12 +152,16 @@ export function provideViewerState() {
|
|
|
152
152
|
}
|
|
153
153
|
let _scrollToPageFn = null;
|
|
154
154
|
let _downloadPdfFn = null;
|
|
155
|
+
let _printPdfFn = null;
|
|
155
156
|
function scrollToPage(pageNumber) {
|
|
156
157
|
_scrollToPageFn?.(pageNumber);
|
|
157
158
|
}
|
|
158
159
|
async function downloadPdf() {
|
|
159
160
|
await _downloadPdfFn?.();
|
|
160
161
|
}
|
|
162
|
+
async function printPdf() {
|
|
163
|
+
await _printPdfFn?.();
|
|
164
|
+
}
|
|
161
165
|
const history = createAnnotationHistory(annotations, painter);
|
|
162
166
|
const state = {
|
|
163
167
|
scale,
|
|
@@ -200,6 +204,7 @@ export function provideViewerState() {
|
|
|
200
204
|
setStylusMode,
|
|
201
205
|
scrollToPage,
|
|
202
206
|
downloadPdf,
|
|
207
|
+
printPdf,
|
|
203
208
|
history,
|
|
204
209
|
readonly,
|
|
205
210
|
shapeDetection: shapeDetectionEnabled,
|
|
@@ -217,6 +222,9 @@ export function provideViewerState() {
|
|
|
217
222
|
setDownloadPdfFn(fn) {
|
|
218
223
|
_downloadPdfFn = fn;
|
|
219
224
|
},
|
|
225
|
+
setPrintPdfFn(fn) {
|
|
226
|
+
_printPdfFn = fn;
|
|
227
|
+
},
|
|
220
228
|
setApplyScaleFn(fn) {
|
|
221
229
|
_applyScaleFn = fn;
|
|
222
230
|
}
|
|
@@ -44,6 +44,10 @@ export declare class KViewerEmbedClient {
|
|
|
44
44
|
skipped: number;
|
|
45
45
|
}>;
|
|
46
46
|
exportPdf(options?: ExportPdfOptions): Promise<Uint8Array>;
|
|
47
|
+
/** Open the viewer's print dialog on the exported (vector) PDF —
|
|
48
|
+
* annotations and form values included, at full print quality.
|
|
49
|
+
* Resolves once the dialog is up, not when printing finishes. */
|
|
50
|
+
printPdf(options?: Omit<ExportPdfOptions, 'download' | 'fileName'>): Promise<void>;
|
|
47
51
|
getFormFieldValues(): Promise<FormFieldValue[]>;
|
|
48
52
|
setFormFieldValue(fieldName: string, value: string | boolean | string[]): Promise<void>;
|
|
49
53
|
addFormField(payload: AddFormFieldPayload): Promise<FormFieldDefinition>;
|
|
@@ -60,6 +60,12 @@ export class KViewerEmbedClient {
|
|
|
60
60
|
exportPdf(options) {
|
|
61
61
|
return this.call("exportPdf", options);
|
|
62
62
|
}
|
|
63
|
+
/** Open the viewer's print dialog on the exported (vector) PDF —
|
|
64
|
+
* annotations and form values included, at full print quality.
|
|
65
|
+
* Resolves once the dialog is up, not when printing finishes. */
|
|
66
|
+
printPdf(options) {
|
|
67
|
+
return this.call("printPdf", options);
|
|
68
|
+
}
|
|
63
69
|
getFormFieldValues() {
|
|
64
70
|
return this.call("getFormFieldValues");
|
|
65
71
|
}
|
|
@@ -17,6 +17,7 @@ export interface KViewerApi {
|
|
|
17
17
|
skipped: number;
|
|
18
18
|
}>;
|
|
19
19
|
exportPdf: (options?: ExportPdfOptions) => Promise<Uint8Array>;
|
|
20
|
+
printPdf: (options?: ExportPdfOptions) => Promise<void>;
|
|
20
21
|
getFormFieldValues: () => FormFieldValue[];
|
|
21
22
|
setFormFieldValue: (fieldName: string, value: string | boolean | string[]) => void;
|
|
22
23
|
addFormField: (payload: AddFormFieldPayload) => FormFieldDefinition;
|
|
@@ -39,6 +40,14 @@ export interface KViewerApi {
|
|
|
39
40
|
getViewedPages: () => number[];
|
|
40
41
|
resetViewedPages: () => void;
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* What the bridge actually requires of a host. Every capability is
|
|
45
|
+
* optional so an embedding application can hand over an adapter exposing
|
|
46
|
+
* only the parts it uses — a real `<KViewer>` ref satisfies this too.
|
|
47
|
+
* Unimplemented methods surface as an `UNSUPPORTED_METHOD_ERROR_NAME`
|
|
48
|
+
* response instead of breaking initialization.
|
|
49
|
+
*/
|
|
50
|
+
export type KViewerHostApi = Partial<KViewerApi>;
|
|
42
51
|
export interface KViewerEmbedBridgeOptions {
|
|
43
52
|
/**
|
|
44
53
|
* Origin of the parent window that hosts this iframe. Used both as
|
|
@@ -67,5 +76,5 @@ interface BridgeHandle {
|
|
|
67
76
|
/** Manually tear down listeners. Called automatically on scope dispose. */
|
|
68
77
|
unmount: () => void;
|
|
69
78
|
}
|
|
70
|
-
export declare function useKViewerEmbedBridge(viewerRef: Ref<
|
|
79
|
+
export declare function useKViewerEmbedBridge(viewerRef: Ref<KViewerHostApi | null | undefined>, options: KViewerEmbedBridgeOptions): BridgeHandle;
|
|
71
80
|
export type { MethodName };
|