@embedpdf/plugin-form 2.14.4 → 3.0.0-next.0
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/LICENSE +192 -21
- package/dist/index.cjs +1416 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +277 -0
- package/dist/index.d.ts +277 -1
- package/dist/index.js +1398 -1779
- package/dist/index.js.map +1 -1
- package/package.json +24 -61
- package/dist/lib/actions.d.ts +0 -33
- package/dist/lib/form-plugin.d.ts +0 -59
- package/dist/lib/handlers/checkbox.handler.d.ts +0 -3
- package/dist/lib/handlers/combobox.handler.d.ts +0 -3
- package/dist/lib/handlers/index.d.ts +0 -5
- package/dist/lib/handlers/listbox.handler.d.ts +0 -3
- package/dist/lib/handlers/radio-button.handler.d.ts +0 -3
- package/dist/lib/handlers/text-field.handler.d.ts +0 -3
- package/dist/lib/index.d.ts +0 -10
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/reducer.d.ts +0 -6
- package/dist/lib/tools.d.ts +0 -457
- package/dist/lib/types.d.ts +0 -98
- package/dist/preact/adapter.d.ts +0 -13
- package/dist/preact/annotation.d.ts +0 -1
- package/dist/preact/core.d.ts +0 -1
- package/dist/preact/index.cjs +0 -2
- package/dist/preact/index.cjs.map +0 -1
- package/dist/preact/index.d.ts +0 -1
- package/dist/preact/index.js +0 -1359
- package/dist/preact/index.js.map +0 -1
- package/dist/react/adapter.d.ts +0 -6
- package/dist/react/annotation.d.ts +0 -1
- package/dist/react/core.d.ts +0 -1
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -1358
- package/dist/react/index.js.map +0 -1
- package/dist/shared/components/annotations/form-checkbox.d.ts +0 -12
- package/dist/shared/components/annotations/form-combobox.d.ts +0 -12
- package/dist/shared/components/annotations/form-listbox.d.ts +0 -12
- package/dist/shared/components/annotations/form-radio-button.d.ts +0 -12
- package/dist/shared/components/annotations/form-text-field.d.ts +0 -12
- package/dist/shared/components/fields/checkbox.d.ts +0 -2
- package/dist/shared/components/fields/combobox.d.ts +0 -2
- package/dist/shared/components/fields/listbox.d.ts +0 -2
- package/dist/shared/components/fields/push-button.d.ts +0 -2
- package/dist/shared/components/fields/radio-button.d.ts +0 -2
- package/dist/shared/components/fields/style.d.ts +0 -6
- package/dist/shared/components/fields/text.d.ts +0 -2
- package/dist/shared/components/fill-mode/combobox-fill-mode.d.ts +0 -3
- package/dist/shared/components/fill-mode/listbox-fill-mode.d.ts +0 -3
- package/dist/shared/components/fill-mode/push-button-fill-mode.d.ts +0 -3
- package/dist/shared/components/fill-mode/radio-button-fill-mode.d.ts +0 -3
- package/dist/shared/components/fill-mode/text-fill-mode.d.ts +0 -3
- package/dist/shared/components/fill-mode/toggle-fill-mode.d.ts +0 -3
- package/dist/shared/components/form-renderer-registration.d.ts +0 -5
- package/dist/shared/components/form-renderers.d.ts +0 -9
- package/dist/shared/components/index.d.ts +0 -2
- package/dist/shared/components/render-widget.d.ts +0 -12
- package/dist/shared/components/types.d.ts +0 -42
- package/dist/shared/hooks/index.d.ts +0 -1
- package/dist/shared/hooks/use-form-widget-state.d.ts +0 -12
- package/dist/shared/hooks/use-form.d.ts +0 -16
- package/dist/shared/index.d.ts +0 -4
- package/dist/shared-preact/components/annotations/form-checkbox.d.ts +0 -12
- package/dist/shared-preact/components/annotations/form-combobox.d.ts +0 -12
- package/dist/shared-preact/components/annotations/form-listbox.d.ts +0 -12
- package/dist/shared-preact/components/annotations/form-radio-button.d.ts +0 -12
- package/dist/shared-preact/components/annotations/form-text-field.d.ts +0 -12
- package/dist/shared-preact/components/fields/checkbox.d.ts +0 -2
- package/dist/shared-preact/components/fields/combobox.d.ts +0 -2
- package/dist/shared-preact/components/fields/listbox.d.ts +0 -2
- package/dist/shared-preact/components/fields/push-button.d.ts +0 -2
- package/dist/shared-preact/components/fields/radio-button.d.ts +0 -2
- package/dist/shared-preact/components/fields/style.d.ts +0 -6
- package/dist/shared-preact/components/fields/text.d.ts +0 -2
- package/dist/shared-preact/components/fill-mode/combobox-fill-mode.d.ts +0 -3
- package/dist/shared-preact/components/fill-mode/listbox-fill-mode.d.ts +0 -3
- package/dist/shared-preact/components/fill-mode/push-button-fill-mode.d.ts +0 -3
- package/dist/shared-preact/components/fill-mode/radio-button-fill-mode.d.ts +0 -3
- package/dist/shared-preact/components/fill-mode/text-fill-mode.d.ts +0 -3
- package/dist/shared-preact/components/fill-mode/toggle-fill-mode.d.ts +0 -3
- package/dist/shared-preact/components/form-renderer-registration.d.ts +0 -5
- package/dist/shared-preact/components/form-renderers.d.ts +0 -9
- package/dist/shared-preact/components/index.d.ts +0 -2
- package/dist/shared-preact/components/render-widget.d.ts +0 -12
- package/dist/shared-preact/components/types.d.ts +0 -42
- package/dist/shared-preact/hooks/index.d.ts +0 -1
- package/dist/shared-preact/hooks/use-form-widget-state.d.ts +0 -12
- package/dist/shared-preact/hooks/use-form.d.ts +0 -16
- package/dist/shared-preact/index.d.ts +0 -4
- package/dist/shared-react/components/annotations/form-checkbox.d.ts +0 -12
- package/dist/shared-react/components/annotations/form-combobox.d.ts +0 -12
- package/dist/shared-react/components/annotations/form-listbox.d.ts +0 -12
- package/dist/shared-react/components/annotations/form-radio-button.d.ts +0 -12
- package/dist/shared-react/components/annotations/form-text-field.d.ts +0 -12
- package/dist/shared-react/components/fields/checkbox.d.ts +0 -2
- package/dist/shared-react/components/fields/combobox.d.ts +0 -2
- package/dist/shared-react/components/fields/listbox.d.ts +0 -2
- package/dist/shared-react/components/fields/push-button.d.ts +0 -2
- package/dist/shared-react/components/fields/radio-button.d.ts +0 -2
- package/dist/shared-react/components/fields/style.d.ts +0 -6
- package/dist/shared-react/components/fields/text.d.ts +0 -2
- package/dist/shared-react/components/fill-mode/combobox-fill-mode.d.ts +0 -3
- package/dist/shared-react/components/fill-mode/listbox-fill-mode.d.ts +0 -3
- package/dist/shared-react/components/fill-mode/push-button-fill-mode.d.ts +0 -3
- package/dist/shared-react/components/fill-mode/radio-button-fill-mode.d.ts +0 -3
- package/dist/shared-react/components/fill-mode/text-fill-mode.d.ts +0 -3
- package/dist/shared-react/components/fill-mode/toggle-fill-mode.d.ts +0 -3
- package/dist/shared-react/components/form-renderer-registration.d.ts +0 -5
- package/dist/shared-react/components/form-renderers.d.ts +0 -9
- package/dist/shared-react/components/index.d.ts +0 -2
- package/dist/shared-react/components/render-widget.d.ts +0 -12
- package/dist/shared-react/components/types.d.ts +0 -42
- package/dist/shared-react/hooks/index.d.ts +0 -1
- package/dist/shared-react/hooks/use-form-widget-state.d.ts +0 -12
- package/dist/shared-react/hooks/use-form.d.ts +0 -16
- package/dist/shared-react/index.d.ts +0 -4
- package/dist/svelte/components/FormRendererRegistration.svelte.d.ts +0 -18
- package/dist/svelte/components/RenderWidget.svelte.d.ts +0 -11
- package/dist/svelte/components/annotations/FormCheckbox.svelte.d.ts +0 -5
- package/dist/svelte/components/annotations/FormCombobox.svelte.d.ts +0 -5
- package/dist/svelte/components/annotations/FormListbox.svelte.d.ts +0 -5
- package/dist/svelte/components/annotations/FormRadioButton.svelte.d.ts +0 -5
- package/dist/svelte/components/annotations/FormTextField.svelte.d.ts +0 -5
- package/dist/svelte/components/fields/ComboboxField.svelte.d.ts +0 -4
- package/dist/svelte/components/fields/ListboxField.svelte.d.ts +0 -4
- package/dist/svelte/components/fields/TextField.svelte.d.ts +0 -4
- package/dist/svelte/components/fill-mode/ComboboxFillMode.svelte.d.ts +0 -5
- package/dist/svelte/components/fill-mode/ListboxFillMode.svelte.d.ts +0 -5
- package/dist/svelte/components/fill-mode/RadioButtonFillMode.svelte.d.ts +0 -5
- package/dist/svelte/components/fill-mode/TextFillMode.svelte.d.ts +0 -5
- package/dist/svelte/components/fill-mode/ToggleFillMode.svelte.d.ts +0 -5
- package/dist/svelte/components/form-renderers.d.ts +0 -9
- package/dist/svelte/components/previews/CheckboxPreview.svelte.d.ts +0 -9
- package/dist/svelte/components/previews/ListboxPreview.svelte.d.ts +0 -9
- package/dist/svelte/components/previews/RadioButtonPreview.svelte.d.ts +0 -9
- package/dist/svelte/components/previews/TextFieldPreview.svelte.d.ts +0 -9
- package/dist/svelte/hooks/index.d.ts +0 -2
- package/dist/svelte/hooks/use-form-widget-state.svelte.d.ts +0 -12
- package/dist/svelte/hooks/use-form.svelte.d.ts +0 -14
- package/dist/svelte/index.cjs +0 -2
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.ts +0 -3
- package/dist/svelte/index.js +0 -1567
- package/dist/svelte/index.js.map +0 -1
- package/dist/vue/components/annotations/form-checkbox.vue.d.ts +0 -6
- package/dist/vue/components/annotations/form-combobox.vue.d.ts +0 -6
- package/dist/vue/components/annotations/form-listbox.vue.d.ts +0 -6
- package/dist/vue/components/annotations/form-radio-button.vue.d.ts +0 -6
- package/dist/vue/components/annotations/form-text-field.vue.d.ts +0 -6
- package/dist/vue/components/fields/combobox-field.vue.d.ts +0 -4
- package/dist/vue/components/fields/listbox-field.vue.d.ts +0 -4
- package/dist/vue/components/fields/style.d.ts +0 -6
- package/dist/vue/components/fields/text-field.vue.d.ts +0 -4
- package/dist/vue/components/fill-mode/combobox-fill-mode.vue.d.ts +0 -6
- package/dist/vue/components/fill-mode/listbox-fill-mode.vue.d.ts +0 -6
- package/dist/vue/components/fill-mode/radio-button-fill-mode.vue.d.ts +0 -6
- package/dist/vue/components/fill-mode/text-fill-mode.vue.d.ts +0 -6
- package/dist/vue/components/fill-mode/toggle-fill-mode.vue.d.ts +0 -6
- package/dist/vue/components/form-renderer-registration.vue.d.ts +0 -13
- package/dist/vue/components/form-renderers.d.ts +0 -3
- package/dist/vue/components/previews/checkbox-preview.vue.d.ts +0 -10
- package/dist/vue/components/previews/listbox-preview.vue.d.ts +0 -10
- package/dist/vue/components/previews/radio-button-preview.vue.d.ts +0 -10
- package/dist/vue/components/previews/text-field-preview.vue.d.ts +0 -10
- package/dist/vue/components/render-widget.vue.d.ts +0 -15
- package/dist/vue/hooks/index.d.ts +0 -2
- package/dist/vue/hooks/use-form-widget-state.d.ts +0 -12
- package/dist/vue/hooks/use-form.d.ts +0 -12
- package/dist/vue/index.cjs +0 -2
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.ts +0 -3
- package/dist/vue/index.js +0 -1643
- package/dist/vue/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1789 +1,1408 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
function selectField(documentId, annotationId) {
|
|
28
|
-
return { type: SELECT_FIELD, payload: { documentId, annotationId } };
|
|
1
|
+
import { createCapabilityToken, definePlugin } from "@embedpdf/core";
|
|
2
|
+
import { AnnotationToken, MIN_DRAG, resolveClickPlacement, widgetAppearanceFromProps } from "@embedpdf/plugin-annotation";
|
|
3
|
+
import { AnnotationToken as AnnotationToken$1 } from "@embedpdf/plugin-annotation/internal";
|
|
4
|
+
import { InteractionToken, samplePointOn } from "@embedpdf/plugin-interaction";
|
|
5
|
+
import { DEFAULT_SCRIPT_BUDGET, javaScriptProgramFromActionTree, scriptFieldsFromSnapshot } from "@embedpdf/core-acrojs";
|
|
6
|
+
//#region src/core/model.ts
|
|
7
|
+
const fieldKeyOf = (field) => field.fieldObjectNumber > 0 ? `obj:${field.fieldObjectNumber}` : `fqn:${field.name}`;
|
|
8
|
+
const initialModel = () => ({
|
|
9
|
+
snapshot: null,
|
|
10
|
+
byKey: {},
|
|
11
|
+
byWidget: {},
|
|
12
|
+
writing: {},
|
|
13
|
+
geom: {},
|
|
14
|
+
seq: 0
|
|
15
|
+
});
|
|
16
|
+
function index(snapshot) {
|
|
17
|
+
const byKey = {};
|
|
18
|
+
const byWidget = {};
|
|
19
|
+
snapshot.fields.forEach((field, i) => {
|
|
20
|
+
byKey[fieldKeyOf(field)] = i;
|
|
21
|
+
for (const w of field.widgets) if (w.annotObjectNumber > 0) byWidget[w.annotObjectNumber] = i;
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
byKey,
|
|
25
|
+
byWidget
|
|
26
|
+
};
|
|
29
27
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
28
|
+
function update(model, msg) {
|
|
29
|
+
switch (msg.t) {
|
|
30
|
+
case "snapshot": return {
|
|
31
|
+
...model,
|
|
32
|
+
snapshot: msg.snapshot,
|
|
33
|
+
...index(msg.snapshot),
|
|
34
|
+
writing: {},
|
|
35
|
+
seq: model.seq + 1
|
|
36
|
+
};
|
|
37
|
+
case "writeStart": return {
|
|
38
|
+
...model,
|
|
39
|
+
writing: {
|
|
40
|
+
...model.writing,
|
|
41
|
+
[msg.key]: true
|
|
42
|
+
},
|
|
43
|
+
seq: model.seq + 1
|
|
44
|
+
};
|
|
45
|
+
case "writeDone": {
|
|
46
|
+
if (!model.snapshot) return model;
|
|
47
|
+
const i = model.byKey[msg.key];
|
|
48
|
+
if (i === void 0) return model;
|
|
49
|
+
const fields = model.snapshot.fields.slice();
|
|
50
|
+
fields[i] = msg.field;
|
|
51
|
+
const { [msg.key]: _done, ...writing } = model.writing;
|
|
52
|
+
const snapshot = {
|
|
53
|
+
...model.snapshot,
|
|
54
|
+
fields
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
...model,
|
|
58
|
+
snapshot,
|
|
59
|
+
...index(snapshot),
|
|
60
|
+
writing,
|
|
61
|
+
seq: model.seq + 1
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
case "writeFailed": {
|
|
65
|
+
const { [msg.key]: _failed, ...writing } = model.writing;
|
|
66
|
+
return {
|
|
67
|
+
...model,
|
|
68
|
+
writing,
|
|
69
|
+
seq: model.seq + 1
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
case "pageGeom": return {
|
|
73
|
+
...model,
|
|
74
|
+
geom: {
|
|
75
|
+
...model.geom,
|
|
76
|
+
[msg.pageObjectNumber]: msg.boxes
|
|
77
|
+
},
|
|
78
|
+
seq: model.seq + 1
|
|
79
|
+
};
|
|
80
|
+
case "clearGeom": {
|
|
81
|
+
if (msg.pageObjectNumber === void 0) return {
|
|
82
|
+
...model,
|
|
83
|
+
geom: {},
|
|
84
|
+
seq: model.seq + 1
|
|
85
|
+
};
|
|
86
|
+
const geom = { ...model.geom };
|
|
87
|
+
delete geom[msg.pageObjectNumber];
|
|
88
|
+
return {
|
|
89
|
+
...model,
|
|
90
|
+
geom,
|
|
91
|
+
seq: model.seq + 1
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
32
95
|
}
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const initialState = {
|
|
37
|
-
documents: {}
|
|
96
|
+
const fieldByKey = (model, key) => {
|
|
97
|
+
const i = model.byKey[key];
|
|
98
|
+
return i === void 0 ? null : model.snapshot?.fields[i] ?? null;
|
|
38
99
|
};
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
...state,
|
|
44
|
-
documents: {
|
|
45
|
-
...state.documents,
|
|
46
|
-
[action.payload.documentId]: action.payload.state
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
case CLEANUP_FORM_STATE: {
|
|
50
|
-
const documentId = action.payload;
|
|
51
|
-
const { [documentId]: _, ...remaining } = state.documents;
|
|
52
|
-
return {
|
|
53
|
-
...state,
|
|
54
|
-
documents: remaining
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
case SELECT_FIELD: {
|
|
58
|
-
const { documentId, annotationId } = action.payload;
|
|
59
|
-
const docState = state.documents[documentId];
|
|
60
|
-
if (!docState) return state;
|
|
61
|
-
return {
|
|
62
|
-
...state,
|
|
63
|
-
documents: {
|
|
64
|
-
...state.documents,
|
|
65
|
-
[documentId]: {
|
|
66
|
-
...docState,
|
|
67
|
-
selectedFieldId: annotationId
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
case DESELECT_FIELD: {
|
|
73
|
-
const documentId = action.payload;
|
|
74
|
-
const docState = state.documents[documentId];
|
|
75
|
-
if (!docState) return state;
|
|
76
|
-
return {
|
|
77
|
-
...state,
|
|
78
|
-
documents: {
|
|
79
|
-
...state.documents,
|
|
80
|
-
[documentId]: {
|
|
81
|
-
...docState,
|
|
82
|
-
selectedFieldId: null
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
default:
|
|
88
|
-
return state;
|
|
89
|
-
}
|
|
100
|
+
const fieldForWidget = (model, annotObjectNumber) => {
|
|
101
|
+
const i = model.byWidget[annotObjectNumber];
|
|
102
|
+
return i === void 0 ? null : model.snapshot?.fields[i] ?? null;
|
|
90
103
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
y: clamp(pos.y, 0, pageSize.height)
|
|
99
|
-
});
|
|
100
|
-
const getDefaults = () => {
|
|
101
|
-
const tool = getTool();
|
|
102
|
-
if (!tool) return null;
|
|
103
|
-
return {
|
|
104
|
-
...tool.defaults,
|
|
105
|
-
fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
|
|
106
|
-
fontSize: tool.defaults.fontSize ?? 12,
|
|
107
|
-
fontColor: tool.defaults.fontColor ?? "#000000",
|
|
108
|
-
strokeColor: tool.defaults.strokeColor ?? "#000000",
|
|
109
|
-
color: tool.defaults.color ?? "#FFFFFF",
|
|
110
|
-
strokeWidth: tool.defaults.strokeWidth ?? 1,
|
|
111
|
-
field: tool.defaults.field
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
const buildRect = (start, end) => {
|
|
115
|
-
const minX = Math.min(start.x, end.x);
|
|
116
|
-
const minY = Math.min(start.y, end.y);
|
|
117
|
-
const width = Math.abs(start.x - end.x);
|
|
118
|
-
const height = Math.abs(start.y - end.y);
|
|
119
|
-
return {
|
|
120
|
-
origin: { x: minX, y: minY },
|
|
121
|
-
size: { width, height }
|
|
122
|
-
};
|
|
123
|
-
};
|
|
124
|
-
const buildAnnotation = (rect) => {
|
|
125
|
-
var _a;
|
|
126
|
-
const defaults = getDefaults();
|
|
127
|
-
const fieldName = `TextField_${uuidV4().slice(0, 8)}`;
|
|
128
|
-
return {
|
|
129
|
-
...defaults,
|
|
130
|
-
type: PdfAnnotationSubtype.WIDGET,
|
|
131
|
-
id: uuidV4(),
|
|
132
|
-
pageIndex,
|
|
133
|
-
rect,
|
|
134
|
-
created: /* @__PURE__ */ new Date(),
|
|
135
|
-
fontFamily: defaults.fontFamily,
|
|
136
|
-
fontSize: defaults.fontSize,
|
|
137
|
-
fontColor: defaults.fontColor,
|
|
138
|
-
strokeColor: defaults.strokeColor,
|
|
139
|
-
color: defaults.color,
|
|
140
|
-
strokeWidth: defaults.strokeWidth,
|
|
141
|
-
field: {
|
|
142
|
-
type: PDF_FORM_FIELD_TYPE.TEXTFIELD,
|
|
143
|
-
flag: ((_a = defaults.field) == null ? void 0 : _a.flag) ?? PDF_FORM_FIELD_FLAG.NONE,
|
|
144
|
-
name: fieldName,
|
|
145
|
-
alternateName: fieldName,
|
|
146
|
-
value: ""
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
};
|
|
150
|
-
const clickDetector = useClickDetector({
|
|
151
|
-
getTool,
|
|
152
|
-
onClickDetected: (pos) => {
|
|
153
|
-
const defaults = getDefaults();
|
|
154
|
-
if (!defaults) return;
|
|
155
|
-
const tool = getTool();
|
|
156
|
-
const clickConfig = tool == null ? void 0 : tool.clickBehavior;
|
|
157
|
-
if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
|
|
158
|
-
const { width, height } = clickConfig.defaultSize;
|
|
159
|
-
const x = clamp(pos.x - width / 2, 0, pageSize.width - width);
|
|
160
|
-
const y = clamp(pos.y - height / 2, 0, pageSize.height - height);
|
|
161
|
-
const rect = {
|
|
162
|
-
origin: { x, y },
|
|
163
|
-
size: { width, height }
|
|
164
|
-
};
|
|
165
|
-
onCommit(buildAnnotation(rect));
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
const getPreview = (current) => {
|
|
169
|
-
const start = getStart();
|
|
170
|
-
if (!start) return null;
|
|
171
|
-
const defaults = getDefaults();
|
|
172
|
-
if (!defaults) return null;
|
|
173
|
-
const rect = buildRect(start, current);
|
|
174
|
-
return {
|
|
175
|
-
type: PdfAnnotationSubtype.WIDGET,
|
|
176
|
-
bounds: rect,
|
|
177
|
-
data: { rect, ...defaults }
|
|
178
|
-
};
|
|
179
|
-
};
|
|
180
|
-
return {
|
|
181
|
-
onPointerDown: (pos, evt) => {
|
|
182
|
-
var _a;
|
|
183
|
-
const clampedPos = clampToPage(pos);
|
|
184
|
-
setStart(clampedPos);
|
|
185
|
-
clickDetector.onStart(clampedPos);
|
|
186
|
-
onPreview(getPreview(clampedPos));
|
|
187
|
-
(_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
|
|
188
|
-
},
|
|
189
|
-
onPointerMove: (pos) => {
|
|
190
|
-
const clampedPos = clampToPage(pos);
|
|
191
|
-
clickDetector.onMove(clampedPos);
|
|
192
|
-
if (getStart() && clickDetector.hasMoved()) {
|
|
193
|
-
onPreview(getPreview(clampedPos));
|
|
194
|
-
}
|
|
195
|
-
},
|
|
196
|
-
onPointerUp: (pos, evt) => {
|
|
197
|
-
var _a;
|
|
198
|
-
const start = getStart();
|
|
199
|
-
if (!start) return;
|
|
200
|
-
const clampedPos = clampToPage(pos);
|
|
201
|
-
if (!clickDetector.hasMoved()) {
|
|
202
|
-
clickDetector.onEnd(clampedPos);
|
|
203
|
-
} else {
|
|
204
|
-
const rect = buildRect(start, clampedPos);
|
|
205
|
-
if (rect.size.width > 5 && rect.size.height > 5) {
|
|
206
|
-
onCommit(buildAnnotation(rect));
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
setStart(null);
|
|
210
|
-
onPreview(null);
|
|
211
|
-
clickDetector.reset();
|
|
212
|
-
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
213
|
-
},
|
|
214
|
-
onPointerLeave: (_, evt) => {
|
|
215
|
-
var _a;
|
|
216
|
-
setStart(null);
|
|
217
|
-
onPreview(null);
|
|
218
|
-
clickDetector.reset();
|
|
219
|
-
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
220
|
-
},
|
|
221
|
-
onPointerCancel: (_, evt) => {
|
|
222
|
-
var _a;
|
|
223
|
-
setStart(null);
|
|
224
|
-
onPreview(null);
|
|
225
|
-
clickDetector.reset();
|
|
226
|
-
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/core/fill-items.ts
|
|
106
|
+
const ZERO_BOX = {
|
|
107
|
+
x: 0,
|
|
108
|
+
y: 0,
|
|
109
|
+
width: 0,
|
|
110
|
+
height: 0
|
|
230
111
|
};
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Project ONE widget of a field into a fill control. `box` is supplied by the
|
|
114
|
+
* caller: the page projection reads the model's widget geometry; a consumer
|
|
115
|
+
* that already owns a live box (the annotation plane's RenderItem) passes it —
|
|
116
|
+
* or nothing, when only the semantics matter. Null for families with no fill
|
|
117
|
+
* control (signature/unknown: rendered by the annotation plane only).
|
|
118
|
+
*/
|
|
119
|
+
function projectWidget(model, field, annotObjectNumber, box = ZERO_BOX) {
|
|
120
|
+
const key = fieldKeyOf(field);
|
|
121
|
+
const base = {
|
|
122
|
+
key,
|
|
123
|
+
annotObjectNumber,
|
|
124
|
+
box,
|
|
125
|
+
disabled: field.flags.readOnly || model.writing[key] === true,
|
|
126
|
+
label: field.alternateName ?? field.name
|
|
127
|
+
};
|
|
128
|
+
switch (field.family) {
|
|
129
|
+
case "text": return {
|
|
130
|
+
...base,
|
|
131
|
+
control: "text",
|
|
132
|
+
value: field.value,
|
|
133
|
+
multiline: field.multiline,
|
|
134
|
+
password: field.password,
|
|
135
|
+
maxLength: field.maxLength,
|
|
136
|
+
comb: field.comb
|
|
137
|
+
};
|
|
138
|
+
case "checkbox": {
|
|
139
|
+
const toggle = field.widgets.find((w) => w.annotObjectNumber === annotObjectNumber);
|
|
140
|
+
return {
|
|
141
|
+
...base,
|
|
142
|
+
control: "toggle",
|
|
143
|
+
kind: "checkbox",
|
|
144
|
+
checked: field.checked,
|
|
145
|
+
onState: toggle && "onState" in toggle ? toggle.onState : "Yes"
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
case "radio": {
|
|
149
|
+
const toggle = field.widgets.find((w) => w.annotObjectNumber === annotObjectNumber);
|
|
150
|
+
return {
|
|
151
|
+
...base,
|
|
152
|
+
control: "toggle",
|
|
153
|
+
kind: "radio",
|
|
154
|
+
checked: toggle && "checked" in toggle ? toggle.checked : false,
|
|
155
|
+
onState: toggle && "onState" in toggle ? toggle.onState : ""
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
case "combobox": return {
|
|
159
|
+
...base,
|
|
160
|
+
control: "choice",
|
|
161
|
+
kind: "combo",
|
|
162
|
+
edit: field.edit,
|
|
163
|
+
multi: false,
|
|
164
|
+
options: field.options,
|
|
165
|
+
selected: field.value === "" ? [] : [field.value]
|
|
166
|
+
};
|
|
167
|
+
case "listbox": return {
|
|
168
|
+
...base,
|
|
169
|
+
control: "choice",
|
|
170
|
+
kind: "list",
|
|
171
|
+
edit: false,
|
|
172
|
+
multi: field.multiSelect,
|
|
173
|
+
options: field.options,
|
|
174
|
+
selected: field.selectedValues
|
|
175
|
+
};
|
|
176
|
+
case "pushbutton": return {
|
|
177
|
+
...base,
|
|
178
|
+
control: "button"
|
|
179
|
+
};
|
|
180
|
+
default: return null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Project one page's widgets into fill controls. Widgets whose geometry has
|
|
185
|
+
* not arrived yet (or that are direct objects with no join key) are skipped —
|
|
186
|
+
* the shell re-runs the projection when `pageGeom` lands.
|
|
187
|
+
*/
|
|
188
|
+
function fillItems(model, pageObjectNumber) {
|
|
189
|
+
const snapshot = model.snapshot;
|
|
190
|
+
const geom = model.geom[pageObjectNumber];
|
|
191
|
+
if (!snapshot || !geom) return [];
|
|
192
|
+
const items = [];
|
|
193
|
+
for (const field of snapshot.fields) for (const widget of field.widgets) {
|
|
194
|
+
if (widget.pageObjectNumber !== pageObjectNumber) continue;
|
|
195
|
+
const box = geom[widget.annotObjectNumber];
|
|
196
|
+
if (!box) continue;
|
|
197
|
+
const item = projectWidget(model, field, widget.annotObjectNumber, box);
|
|
198
|
+
if (item) items.push(item);
|
|
199
|
+
}
|
|
200
|
+
return items;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Project a SINGLE widget by its annotation object number — the join the
|
|
204
|
+
* annotation-plane render layer uses (it owns a live box already, so the
|
|
205
|
+
* model's cached geometry is used only when it happens to be loaded; the
|
|
206
|
+
* item's semantics never depend on it). Null while the snapshot hasn't
|
|
207
|
+
* landed, or for families with no fill control.
|
|
208
|
+
*/
|
|
209
|
+
function fillItemForWidget(model, annotObjectNumber) {
|
|
210
|
+
const field = fieldForWidget(model, annotObjectNumber);
|
|
211
|
+
if (!field) return null;
|
|
212
|
+
const widget = field.widgets.find((w) => w.annotObjectNumber === annotObjectNumber);
|
|
213
|
+
if (!widget) return null;
|
|
214
|
+
const box = model.geom[widget.pageObjectNumber]?.[annotObjectNumber];
|
|
215
|
+
return projectWidget(model, field, annotObjectNumber, box);
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/mutationQueue.ts
|
|
219
|
+
/** Small promise-tail queue: failures never poison later mutations. */
|
|
220
|
+
function createSerialMutationQueue() {
|
|
221
|
+
let tail = Promise.resolve();
|
|
222
|
+
return (operation) => {
|
|
223
|
+
const result = tail.then(operation, operation);
|
|
224
|
+
tail = result.then(() => void 0, () => void 0);
|
|
225
|
+
return result;
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/scripting.ts
|
|
230
|
+
const refKey = (ref) => ref.kind === "objectNumber" ? `obj:${ref.fieldObjectNumber}` : `fqn:${ref.name}`;
|
|
231
|
+
const sameRef = (left, right) => refKey(left) === refKey(right);
|
|
232
|
+
const cloneValue = (value) => Array.isArray(value) ? [...value] : value;
|
|
233
|
+
const sameValue = (left, right) => Array.isArray(left) && Array.isArray(right) ? left.length === right.length && left.every((value, index) => value === right[index]) : left === right;
|
|
234
|
+
function cloneFields(fields) {
|
|
235
|
+
return fields.map((field) => ({
|
|
236
|
+
...field,
|
|
237
|
+
ref: { ...field.ref },
|
|
238
|
+
value: cloneValue(field.value),
|
|
239
|
+
defaultValue: cloneValue(field.defaultValue),
|
|
240
|
+
...field.options ? { options: field.options.map((option) => ({ ...option })) } : {}
|
|
241
|
+
}));
|
|
242
|
+
}
|
|
243
|
+
function fieldByRef(fields, ref) {
|
|
244
|
+
return fields.find((field) => sameRef(field.ref, ref));
|
|
245
|
+
}
|
|
246
|
+
function snapshotField(snapshot, ref) {
|
|
247
|
+
return snapshot.fields.find((field) => sameRef(field.ref, ref));
|
|
248
|
+
}
|
|
249
|
+
function scriptValueFromFormValue(field, value) {
|
|
250
|
+
if (field.family === "text" && value.type === "text") return value.value;
|
|
251
|
+
if ((field.family === "checkbox" || field.family === "radio") && value.type === "toggle") return value.state ?? "Off";
|
|
252
|
+
if (field.family === "combobox" && value.type === "choice") return value.values[0] ?? "";
|
|
253
|
+
if (field.family === "listbox" && value.type === "choice") return [...value.values];
|
|
254
|
+
throw new Error(`Form value type '${value.type}' does not match field family '${field.family}'`);
|
|
255
|
+
}
|
|
256
|
+
function formValueFromScriptValue(field) {
|
|
257
|
+
switch (field.family) {
|
|
258
|
+
case "text": return {
|
|
259
|
+
type: "text",
|
|
260
|
+
value: String(field.value ?? "")
|
|
261
|
+
};
|
|
262
|
+
case "checkbox":
|
|
263
|
+
case "radio": return {
|
|
264
|
+
type: "toggle",
|
|
265
|
+
state: field.value === null || field.value === void 0 || String(field.value) === "Off" ? null : String(field.value)
|
|
266
|
+
};
|
|
267
|
+
case "combobox": return {
|
|
268
|
+
type: "choice",
|
|
269
|
+
values: Array.isArray(field.value) ? field.value.slice(0, 1) : [String(field.value ?? "")]
|
|
270
|
+
};
|
|
271
|
+
case "listbox": return {
|
|
272
|
+
type: "choice",
|
|
273
|
+
values: Array.isArray(field.value) ? [...field.value] : [String(field.value ?? "")]
|
|
274
|
+
};
|
|
275
|
+
default: return null;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function applyEffects(overlay, effects) {
|
|
279
|
+
for (const effect of effects) {
|
|
280
|
+
if (effect.kind === "reset") {
|
|
281
|
+
for (const ref of effect.refs) {
|
|
282
|
+
const field = fieldByRef(overlay.fields, ref);
|
|
283
|
+
if (!field) continue;
|
|
284
|
+
field.value = cloneValue(field.defaultValue);
|
|
285
|
+
overlay.resetKeys.add(refKey(ref));
|
|
286
|
+
}
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
const field = fieldByRef(overlay.fields, effect.ref);
|
|
290
|
+
if (!field) continue;
|
|
291
|
+
const key = refKey(effect.ref);
|
|
292
|
+
if (effect.kind === "setValue") {
|
|
293
|
+
field.value = scriptValueFromFormValue(field, effect.value);
|
|
294
|
+
overlay.resetKeys.delete(key);
|
|
295
|
+
} else if (effect.kind === "setDisplay") field.display = effect.display;
|
|
296
|
+
else overlay.appearances.set(key, {
|
|
297
|
+
ref: effect.ref,
|
|
298
|
+
text: effect.text
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function canonicalEffects(overlay) {
|
|
303
|
+
const effects = [];
|
|
304
|
+
const resets = [];
|
|
305
|
+
for (const field of overlay.fields) {
|
|
306
|
+
const original = fieldByRef(overlay.original, field.ref);
|
|
307
|
+
if (!original) continue;
|
|
308
|
+
if (!sameValue(original.value, field.value)) {
|
|
309
|
+
const key = refKey(field.ref);
|
|
310
|
+
if (overlay.resetKeys.has(key) && sameValue(field.value, field.defaultValue)) resets.push(field.ref);
|
|
311
|
+
else {
|
|
312
|
+
const value = formValueFromScriptValue(field);
|
|
313
|
+
if (value) effects.push({
|
|
314
|
+
kind: "setValue",
|
|
315
|
+
ref: field.ref,
|
|
316
|
+
value
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (original.display !== field.display) effects.push({
|
|
321
|
+
kind: "setDisplay",
|
|
322
|
+
ref: field.ref,
|
|
323
|
+
display: field.display
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (resets.length > 0) effects.unshift({
|
|
327
|
+
kind: "reset",
|
|
328
|
+
refs: resets
|
|
329
|
+
});
|
|
330
|
+
effects.push(...Array.from(overlay.appearances.values(), ({ ref, text }) => ({
|
|
331
|
+
kind: "setAppearanceText",
|
|
332
|
+
ref,
|
|
333
|
+
text
|
|
334
|
+
})));
|
|
335
|
+
return effects;
|
|
336
|
+
}
|
|
337
|
+
function scriptError(message) {
|
|
338
|
+
return {
|
|
339
|
+
kind: "invalid-output",
|
|
340
|
+
message
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function utf8Length(value) {
|
|
344
|
+
let length = 0;
|
|
345
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
346
|
+
const code = value.charCodeAt(index);
|
|
347
|
+
if (code < 128) length += 1;
|
|
348
|
+
else if (code < 2048) length += 2;
|
|
349
|
+
else if (code >= 55296 && code <= 56319 && index + 1 < value.length) {
|
|
350
|
+
length += 4;
|
|
351
|
+
index += 1;
|
|
352
|
+
} else length += 3;
|
|
353
|
+
}
|
|
354
|
+
return length;
|
|
355
|
+
}
|
|
356
|
+
function seedFrom(documentId, sequence) {
|
|
357
|
+
let hash = 2166136261;
|
|
358
|
+
for (const char of `${documentId}:${sequence}`) {
|
|
359
|
+
hash ^= char.charCodeAt(0);
|
|
360
|
+
hash = Math.imul(hash, 16777619);
|
|
361
|
+
}
|
|
362
|
+
return hash >>> 0;
|
|
363
|
+
}
|
|
364
|
+
function resolvedIdentity(doc, config) {
|
|
365
|
+
const claims = doc.security.identity;
|
|
366
|
+
const supplied = typeof config.identity === "function" ? config.identity() : config.identity ?? {};
|
|
367
|
+
return {
|
|
368
|
+
name: supplied.name ?? claims?.display_name ?? claims?.user_id ?? "",
|
|
369
|
+
loginName: supplied.loginName ?? claims?.user_id ?? "",
|
|
370
|
+
corporation: supplied.corporation ?? claims?.group_id ?? "",
|
|
371
|
+
email: supplied.email ?? ""
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function pageNumberFor(meta, field) {
|
|
375
|
+
const pon = field.widgets.find((widget) => widget.pageObjectNumber > 0)?.pageObjectNumber;
|
|
376
|
+
if (!meta || pon === void 0) return 0;
|
|
377
|
+
const index = meta.pages.findIndex((page) => page.pageObjectNumber === pon);
|
|
378
|
+
return Math.max(0, index);
|
|
379
|
+
}
|
|
380
|
+
function statusFromEffects(result) {
|
|
381
|
+
if (result.results.some(({ status }) => status === "failed" || status === "skipped")) return "failed";
|
|
382
|
+
if (result.results.some(({ status }) => status === "rejected")) return "failed";
|
|
383
|
+
return result.results.some(({ status }) => status === "applied") ? "applied" : "unchanged";
|
|
384
|
+
}
|
|
385
|
+
var FormScriptingController = class {
|
|
386
|
+
constructor(options) {
|
|
387
|
+
this.options = options;
|
|
388
|
+
this.sandboxPromise = null;
|
|
389
|
+
this.booted = false;
|
|
390
|
+
this.disposed = false;
|
|
391
|
+
this.sequence = 0;
|
|
392
|
+
}
|
|
393
|
+
dispose() {
|
|
394
|
+
if (this.disposed) return;
|
|
395
|
+
this.disposed = true;
|
|
396
|
+
this.sandboxPromise?.then((sandbox) => sandbox.dispose()).catch(() => void 0);
|
|
397
|
+
}
|
|
398
|
+
async commit(snapshot, ref, proposed) {
|
|
399
|
+
return this.transact(snapshot, ref, proposed);
|
|
400
|
+
}
|
|
401
|
+
/** Execute one originating widget's activation action (`/A`, including `/Next`). */
|
|
402
|
+
async activate(snapshot, ref, action) {
|
|
403
|
+
return this.transact(snapshot, ref, void 0, action);
|
|
404
|
+
}
|
|
405
|
+
/** Run lazy document boot plus the `/CO` chain without a user field change. */
|
|
406
|
+
async recalculate(snapshot) {
|
|
407
|
+
const target = snapshot.calculationOrder.find((ref) => ref !== null && snapshotField(snapshot, ref) !== void 0) ?? snapshot.fields[0]?.ref;
|
|
408
|
+
if (!target) return {
|
|
409
|
+
status: "unchanged",
|
|
410
|
+
scripted: true,
|
|
411
|
+
effectsResult: null,
|
|
412
|
+
uiEffects: [],
|
|
413
|
+
diagnostics: []
|
|
414
|
+
};
|
|
415
|
+
return this.transact(snapshot, target);
|
|
416
|
+
}
|
|
417
|
+
async transact(snapshot, ref, proposed, activation) {
|
|
418
|
+
if (this.disposed) throw new Error("Form scripting controller is disposed");
|
|
419
|
+
const target = snapshotField(snapshot, ref);
|
|
420
|
+
if (!target) return this.failed([], [], scriptError(`Form field '${refKey(ref)}' no longer exists`));
|
|
421
|
+
const original = scriptFieldsFromSnapshot(snapshot);
|
|
422
|
+
const overlay = {
|
|
423
|
+
original,
|
|
424
|
+
fields: cloneFields(original),
|
|
425
|
+
resetKeys: /* @__PURE__ */ new Set(),
|
|
426
|
+
appearances: /* @__PURE__ */ new Map()
|
|
427
|
+
};
|
|
428
|
+
const targetInput = fieldByRef(overlay.fields, ref);
|
|
429
|
+
if (!targetInput) return this.failed([], [], scriptError(`Form field '${refKey(ref)}' has no script view`));
|
|
430
|
+
const hasProposedValue = proposed !== void 0;
|
|
431
|
+
let proposedValue = cloneValue(targetInput.value);
|
|
432
|
+
if (proposed) try {
|
|
433
|
+
proposedValue = scriptValueFromFormValue(targetInput, proposed);
|
|
434
|
+
} catch (error) {
|
|
435
|
+
return this.failed([], [], scriptError(error instanceof Error ? error.message : String(error)));
|
|
436
|
+
}
|
|
437
|
+
const uiEffects = [];
|
|
438
|
+
const diagnostics = [];
|
|
439
|
+
const meta = this.options.document();
|
|
440
|
+
const nowMs = this.options.config.now?.() ?? Date.now();
|
|
441
|
+
const inputBase = {
|
|
442
|
+
document: {
|
|
443
|
+
id: this.options.doc.id,
|
|
444
|
+
fileName: this.options.config.fileName?.() ?? meta?.name ?? this.options.doc.id,
|
|
445
|
+
pageCount: meta?.pageCount ?? 0,
|
|
446
|
+
pageNumber: pageNumberFor(meta, target)
|
|
447
|
+
},
|
|
448
|
+
identity: resolvedIdentity(this.options.doc, this.options.config),
|
|
449
|
+
environment: {
|
|
450
|
+
nowMs,
|
|
451
|
+
utcOffsetMinutes: this.options.config.utcOffsetMinutes?.() ?? -new Date(nowMs).getTimezoneOffset(),
|
|
452
|
+
randomSeed: this.options.config.randomSeed?.() ?? seedFrom(this.options.doc.id, this.sequence++)
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
const budget = this.options.config.budget ?? DEFAULT_SCRIPT_BUDGET;
|
|
456
|
+
let executionMs = 0;
|
|
457
|
+
let aggregateOutputSize = 0;
|
|
458
|
+
const consume = (output, phase) => {
|
|
459
|
+
aggregateOutputSize += utf8Length(JSON.stringify(output));
|
|
460
|
+
uiEffects.push(...output.uiEffects.map((effect) => ({
|
|
461
|
+
...effect,
|
|
462
|
+
phase
|
|
463
|
+
})));
|
|
464
|
+
diagnostics.push(...output.diagnostics);
|
|
465
|
+
if (aggregateOutputSize > budget.maxOutputBytes) return {
|
|
466
|
+
kind: "budget",
|
|
467
|
+
message: `Form script transaction output exceeded ${budget.maxOutputBytes} bytes`
|
|
468
|
+
};
|
|
469
|
+
return output.error ?? null;
|
|
470
|
+
};
|
|
471
|
+
const remainingBudget = () => {
|
|
472
|
+
const remaining = budget.maxExecutionMs - executionMs;
|
|
473
|
+
return remaining <= 0 ? null : {
|
|
474
|
+
...budget,
|
|
475
|
+
maxExecutionMs: remaining
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
const run = async (kind, source, input) => {
|
|
479
|
+
const remaining = remainingBudget();
|
|
480
|
+
if (!remaining) return { error: {
|
|
481
|
+
kind: "budget",
|
|
482
|
+
message: "Form script transaction timed out"
|
|
483
|
+
} };
|
|
484
|
+
let output;
|
|
485
|
+
try {
|
|
486
|
+
const sandbox = await this.sandbox();
|
|
487
|
+
const startedAt = Date.now();
|
|
488
|
+
output = kind === "boot" ? sandbox.boot(source, input, remaining) : sandbox.run(source, input, remaining);
|
|
489
|
+
executionMs += Date.now() - startedAt;
|
|
490
|
+
} catch (error) {
|
|
491
|
+
return { error: scriptError(error instanceof Error ? error.message : String(error)) };
|
|
492
|
+
}
|
|
493
|
+
const error = consume(output, kind === "boot" ? "boot" : "user");
|
|
494
|
+
return error ? { error } : { output };
|
|
495
|
+
};
|
|
496
|
+
if (!this.booted) {
|
|
497
|
+
this.booted = true;
|
|
498
|
+
let sources = [];
|
|
499
|
+
let bootError = null;
|
|
500
|
+
try {
|
|
501
|
+
sources = (this.options.doc.actions ? await this.options.doc.actions.read() : null)?.nameTreeScripts.map(({ action }) => javaScriptProgramFromActionTree(action)) ?? [];
|
|
502
|
+
} catch (error) {
|
|
503
|
+
bootError = scriptError(error instanceof Error ? error.message : String(error));
|
|
504
|
+
}
|
|
505
|
+
if (!bootError && sources.length > 0) {
|
|
506
|
+
const boot = await run("boot", sources, {
|
|
507
|
+
...inputBase,
|
|
508
|
+
fields: overlay.fields,
|
|
509
|
+
event: { kind: "name-tree-boot" }
|
|
510
|
+
});
|
|
511
|
+
if (boot.error) bootError = boot.error;
|
|
512
|
+
else applyEffects(overlay, boot.output.formEffects);
|
|
513
|
+
}
|
|
514
|
+
if (bootError) diagnostics.push({
|
|
515
|
+
code: "script-error",
|
|
516
|
+
message: `Document boot script failed (continuing without it): ${bootError.message}`
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
const bootEffects = canonicalEffects(overlay);
|
|
520
|
+
if (activation) {
|
|
521
|
+
let program;
|
|
522
|
+
try {
|
|
523
|
+
program = javaScriptProgramFromActionTree(activation);
|
|
524
|
+
} catch (error) {
|
|
525
|
+
return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
|
|
526
|
+
}
|
|
527
|
+
if (program) {
|
|
528
|
+
const activated = await run("run", program, {
|
|
529
|
+
...inputBase,
|
|
530
|
+
fields: overlay.fields,
|
|
531
|
+
event: {
|
|
532
|
+
kind: "widget-activate",
|
|
533
|
+
target: ref,
|
|
534
|
+
source: ref,
|
|
535
|
+
value: targetInput.value
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
if (activated.error) return this.failed(uiEffects, diagnostics, activated.error);
|
|
539
|
+
applyEffects(overlay, activated.output.formEffects);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (!activation && hasProposedValue && target.actions?.keystroke && target.family === "text") {
|
|
543
|
+
let program;
|
|
544
|
+
try {
|
|
545
|
+
program = javaScriptProgramFromActionTree(target.actions.keystroke);
|
|
546
|
+
} catch (error) {
|
|
547
|
+
return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
|
|
548
|
+
}
|
|
549
|
+
if (program) {
|
|
550
|
+
const oldValue = String(targetInput.value ?? "");
|
|
551
|
+
const key = await run("run", program, {
|
|
552
|
+
...inputBase,
|
|
553
|
+
fields: overlay.fields,
|
|
554
|
+
event: {
|
|
555
|
+
kind: "field-keystroke",
|
|
556
|
+
target: ref,
|
|
557
|
+
source: ref,
|
|
558
|
+
value: oldValue,
|
|
559
|
+
change: String(proposedValue ?? ""),
|
|
560
|
+
selStart: 0,
|
|
561
|
+
selEnd: oldValue.length,
|
|
562
|
+
willCommit: true
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
if (key.error) return this.failed(uiEffects, diagnostics, key.error);
|
|
566
|
+
if (!key.output.event.rc) return this.finishRejected(bootEffects, uiEffects, diagnostics);
|
|
567
|
+
applyEffects(overlay, key.output.formEffects);
|
|
568
|
+
const event = key.output.event;
|
|
569
|
+
const base = String(event.value ?? "");
|
|
570
|
+
proposedValue = base.slice(0, event.selStart) + event.change + base.slice(Math.max(event.selStart, event.selEnd));
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (!activation && hasProposedValue) {
|
|
574
|
+
targetInput.value = cloneValue(proposedValue);
|
|
575
|
+
overlay.resetKeys.delete(refKey(ref));
|
|
576
|
+
}
|
|
577
|
+
if (!activation && hasProposedValue && target.actions?.validate) {
|
|
578
|
+
let program;
|
|
579
|
+
try {
|
|
580
|
+
program = javaScriptProgramFromActionTree(target.actions.validate);
|
|
581
|
+
} catch (error) {
|
|
582
|
+
return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
|
|
583
|
+
}
|
|
584
|
+
if (program) {
|
|
585
|
+
const validation = await run("run", program, {
|
|
586
|
+
...inputBase,
|
|
587
|
+
fields: overlay.fields,
|
|
588
|
+
event: {
|
|
589
|
+
kind: "field-validate",
|
|
590
|
+
target: ref,
|
|
591
|
+
source: ref,
|
|
592
|
+
value: proposedValue,
|
|
593
|
+
willCommit: true
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
if (validation.error) return this.failed(uiEffects, diagnostics, validation.error);
|
|
597
|
+
if (!validation.output.event.rc) return this.finishRejected(bootEffects, uiEffects, diagnostics);
|
|
598
|
+
targetInput.value = cloneValue(validation.output.event.value);
|
|
599
|
+
applyEffects(overlay, validation.output.formEffects);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
const formatRefs = !activation && hasProposedValue ? [ref] : [];
|
|
603
|
+
for (const calculationRef of activation ? [] : snapshot.calculationOrder) {
|
|
604
|
+
if (!calculationRef) continue;
|
|
605
|
+
const calculated = snapshotField(snapshot, calculationRef);
|
|
606
|
+
const tree = calculated?.actions?.calculate;
|
|
607
|
+
if (!calculated || !tree) continue;
|
|
608
|
+
let program;
|
|
609
|
+
try {
|
|
610
|
+
program = javaScriptProgramFromActionTree(tree);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
|
|
613
|
+
}
|
|
614
|
+
const calculatedInput = fieldByRef(overlay.fields, calculationRef);
|
|
615
|
+
if (program && calculatedInput) {
|
|
616
|
+
const calculation = await run("run", program, {
|
|
617
|
+
...inputBase,
|
|
618
|
+
fields: overlay.fields,
|
|
619
|
+
event: {
|
|
620
|
+
kind: "field-calculate",
|
|
621
|
+
target: calculationRef,
|
|
622
|
+
...hasProposedValue ? { source: ref } : {},
|
|
623
|
+
value: calculatedInput.value
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
if (calculation.error) return this.failed(uiEffects, diagnostics, calculation.error);
|
|
627
|
+
applyEffects(overlay, calculation.output.formEffects);
|
|
628
|
+
}
|
|
629
|
+
if (!formatRefs.some((candidate) => sameRef(candidate, calculationRef))) formatRefs.push(calculationRef);
|
|
630
|
+
}
|
|
631
|
+
for (const formatRef of formatRefs) {
|
|
632
|
+
const field = snapshotField(snapshot, formatRef);
|
|
633
|
+
const tree = field?.actions?.format;
|
|
634
|
+
const overlayField = fieldByRef(overlay.fields, formatRef);
|
|
635
|
+
if (!field || !tree || !overlayField) continue;
|
|
636
|
+
let program;
|
|
637
|
+
try {
|
|
638
|
+
program = javaScriptProgramFromActionTree(tree);
|
|
639
|
+
} catch (error) {
|
|
640
|
+
return this.failed(uiEffects, diagnostics, scriptError(error instanceof Error ? error.message : String(error)));
|
|
641
|
+
}
|
|
642
|
+
if (!program) continue;
|
|
643
|
+
const format = await run("run", program, {
|
|
644
|
+
...inputBase,
|
|
645
|
+
fields: overlay.fields,
|
|
646
|
+
event: {
|
|
647
|
+
kind: "field-format",
|
|
648
|
+
target: formatRef,
|
|
649
|
+
...hasProposedValue ? { source: ref } : {},
|
|
650
|
+
value: overlayField.value
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
if (format.error) return this.failed(uiEffects, diagnostics, format.error);
|
|
654
|
+
applyEffects(overlay, format.output.formEffects);
|
|
655
|
+
}
|
|
656
|
+
const effects = canonicalEffects(overlay);
|
|
657
|
+
if (effects.length + uiEffects.length > budget.maxEffects) return this.failed(uiEffects, diagnostics, {
|
|
658
|
+
kind: "budget",
|
|
659
|
+
message: `Form script transaction exceeded ${budget.maxEffects} effects`
|
|
660
|
+
});
|
|
661
|
+
if (effects.length === 0) return {
|
|
662
|
+
status: "unchanged",
|
|
663
|
+
scripted: true,
|
|
664
|
+
effectsResult: null,
|
|
665
|
+
uiEffects,
|
|
666
|
+
diagnostics
|
|
667
|
+
};
|
|
668
|
+
if (!this.options.doc.forms.applyEffects) return this.failed(uiEffects, diagnostics, scriptError("This engine does not support batched form effects"));
|
|
669
|
+
const effectsResult = await this.options.doc.forms.applyEffects(effects);
|
|
670
|
+
const status = statusFromEffects(effectsResult);
|
|
671
|
+
return {
|
|
672
|
+
status,
|
|
673
|
+
scripted: true,
|
|
674
|
+
effectsResult,
|
|
675
|
+
uiEffects,
|
|
676
|
+
diagnostics,
|
|
677
|
+
...status === "failed" ? { error: scriptError("One or more native form effects failed") } : {}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
async sandbox() {
|
|
681
|
+
this.sandboxPromise ?? (this.sandboxPromise = this.options.sandboxFactory().then((sandbox) => {
|
|
682
|
+
if (this.disposed) sandbox.dispose();
|
|
683
|
+
return sandbox;
|
|
684
|
+
}));
|
|
685
|
+
return this.sandboxPromise;
|
|
686
|
+
}
|
|
687
|
+
failed(uiEffects, diagnostics, error) {
|
|
688
|
+
return {
|
|
689
|
+
status: "failed",
|
|
690
|
+
scripted: true,
|
|
691
|
+
effectsResult: null,
|
|
692
|
+
uiEffects,
|
|
693
|
+
diagnostics,
|
|
694
|
+
error
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
async finishRejected(bootEffects, uiEffects, diagnostics) {
|
|
698
|
+
if (bootEffects.length === 0) return {
|
|
699
|
+
status: "rejected",
|
|
700
|
+
scripted: true,
|
|
701
|
+
effectsResult: null,
|
|
702
|
+
uiEffects,
|
|
703
|
+
diagnostics
|
|
704
|
+
};
|
|
705
|
+
if (!this.options.doc.forms.applyEffects) return this.failed(uiEffects, diagnostics, scriptError("Batched form effects are unavailable"));
|
|
706
|
+
const effectsResult = await this.options.doc.forms.applyEffects(bootEffects);
|
|
707
|
+
if (statusFromEffects(effectsResult) === "failed") return this.failed(uiEffects, diagnostics, scriptError("A name-tree boot effect failed"));
|
|
708
|
+
return {
|
|
709
|
+
status: "rejected",
|
|
710
|
+
scripted: true,
|
|
711
|
+
effectsResult,
|
|
712
|
+
uiEffects,
|
|
713
|
+
diagnostics
|
|
714
|
+
};
|
|
715
|
+
}
|
|
505
716
|
};
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
strokeColor: tool.defaults.strokeColor ?? "#000000",
|
|
524
|
-
color: tool.defaults.color ?? "#FFFFFF",
|
|
525
|
-
strokeWidth: tool.defaults.strokeWidth ?? 1,
|
|
526
|
-
field: tool.defaults.field
|
|
527
|
-
};
|
|
528
|
-
};
|
|
529
|
-
const buildRect = (start, end) => {
|
|
530
|
-
const minX = Math.min(start.x, end.x);
|
|
531
|
-
const minY = Math.min(start.y, end.y);
|
|
532
|
-
const width = Math.abs(start.x - end.x);
|
|
533
|
-
const height = Math.abs(start.y - end.y);
|
|
534
|
-
return {
|
|
535
|
-
origin: { x: minX, y: minY },
|
|
536
|
-
size: { width, height }
|
|
537
|
-
};
|
|
538
|
-
};
|
|
539
|
-
const buildAnnotation = (rect) => {
|
|
540
|
-
var _a;
|
|
541
|
-
const defaults = getDefaults();
|
|
542
|
-
const fieldName = `Combobox_${uuidV4().slice(0, 8)}`;
|
|
543
|
-
return {
|
|
544
|
-
...defaults,
|
|
545
|
-
type: PdfAnnotationSubtype.WIDGET,
|
|
546
|
-
id: uuidV4(),
|
|
547
|
-
pageIndex,
|
|
548
|
-
rect,
|
|
549
|
-
created: /* @__PURE__ */ new Date(),
|
|
550
|
-
fontFamily: defaults.fontFamily,
|
|
551
|
-
fontSize: defaults.fontSize,
|
|
552
|
-
fontColor: defaults.fontColor,
|
|
553
|
-
strokeColor: defaults.strokeColor,
|
|
554
|
-
color: defaults.color,
|
|
555
|
-
strokeWidth: defaults.strokeWidth,
|
|
556
|
-
field: {
|
|
557
|
-
type: PDF_FORM_FIELD_TYPE.COMBOBOX,
|
|
558
|
-
flag: ((_a = defaults.field) == null ? void 0 : _a.flag) ?? PDF_FORM_FIELD_FLAG.NONE,
|
|
559
|
-
name: fieldName,
|
|
560
|
-
alternateName: fieldName,
|
|
561
|
-
value: "",
|
|
562
|
-
options: [
|
|
563
|
-
{ label: "Option 1", isSelected: true },
|
|
564
|
-
{ label: "Option 2", isSelected: false },
|
|
565
|
-
{ label: "Option 3", isSelected: false }
|
|
566
|
-
]
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
};
|
|
570
|
-
const clickDetector = useClickDetector({
|
|
571
|
-
getTool,
|
|
572
|
-
onClickDetected: (pos) => {
|
|
573
|
-
const defaults = getDefaults();
|
|
574
|
-
if (!defaults) return;
|
|
575
|
-
const tool = getTool();
|
|
576
|
-
const clickConfig = tool == null ? void 0 : tool.clickBehavior;
|
|
577
|
-
if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
|
|
578
|
-
const { width, height } = clickConfig.defaultSize;
|
|
579
|
-
const x = clamp(pos.x - width / 2, 0, pageSize.width - width);
|
|
580
|
-
const y = clamp(pos.y - height / 2, 0, pageSize.height - height);
|
|
581
|
-
const rect = {
|
|
582
|
-
origin: { x, y },
|
|
583
|
-
size: { width, height }
|
|
584
|
-
};
|
|
585
|
-
onCommit(buildAnnotation(rect));
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
const getPreview = (current) => {
|
|
589
|
-
const start = getStart();
|
|
590
|
-
if (!start) return null;
|
|
591
|
-
const defaults = getDefaults();
|
|
592
|
-
if (!defaults) return null;
|
|
593
|
-
const rect = buildRect(start, current);
|
|
594
|
-
return {
|
|
595
|
-
type: PdfAnnotationSubtype.WIDGET,
|
|
596
|
-
bounds: rect,
|
|
597
|
-
data: { rect, ...defaults }
|
|
598
|
-
};
|
|
599
|
-
};
|
|
600
|
-
return {
|
|
601
|
-
onPointerDown: (pos, evt) => {
|
|
602
|
-
var _a;
|
|
603
|
-
const clampedPos = clampToPage(pos);
|
|
604
|
-
setStart(clampedPos);
|
|
605
|
-
clickDetector.onStart(clampedPos);
|
|
606
|
-
onPreview(getPreview(clampedPos));
|
|
607
|
-
(_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
|
|
608
|
-
},
|
|
609
|
-
onPointerMove: (pos) => {
|
|
610
|
-
const clampedPos = clampToPage(pos);
|
|
611
|
-
clickDetector.onMove(clampedPos);
|
|
612
|
-
if (getStart() && clickDetector.hasMoved()) {
|
|
613
|
-
onPreview(getPreview(clampedPos));
|
|
614
|
-
}
|
|
615
|
-
},
|
|
616
|
-
onPointerUp: (pos, evt) => {
|
|
617
|
-
var _a;
|
|
618
|
-
const start = getStart();
|
|
619
|
-
if (!start) return;
|
|
620
|
-
const clampedPos = clampToPage(pos);
|
|
621
|
-
if (!clickDetector.hasMoved()) {
|
|
622
|
-
clickDetector.onEnd(clampedPos);
|
|
623
|
-
} else {
|
|
624
|
-
const rect = buildRect(start, clampedPos);
|
|
625
|
-
if (rect.size.width > 5 && rect.size.height > 5) {
|
|
626
|
-
onCommit(buildAnnotation(rect));
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
setStart(null);
|
|
630
|
-
onPreview(null);
|
|
631
|
-
clickDetector.reset();
|
|
632
|
-
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
633
|
-
},
|
|
634
|
-
onPointerLeave: (_, evt) => {
|
|
635
|
-
var _a;
|
|
636
|
-
setStart(null);
|
|
637
|
-
onPreview(null);
|
|
638
|
-
clickDetector.reset();
|
|
639
|
-
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
640
|
-
},
|
|
641
|
-
onPointerCancel: (_, evt) => {
|
|
642
|
-
var _a;
|
|
643
|
-
setStart(null);
|
|
644
|
-
onPreview(null);
|
|
645
|
-
clickDetector.reset();
|
|
646
|
-
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
}
|
|
717
|
+
function createFormScriptingController(options) {
|
|
718
|
+
return new FormScriptingController(options);
|
|
719
|
+
}
|
|
720
|
+
//#endregion
|
|
721
|
+
//#region src/capability.ts
|
|
722
|
+
/** PDF user-space rect (y-up) → content-space box (y-down, crop-relative). */
|
|
723
|
+
const toBox = (rect, crop) => ({
|
|
724
|
+
x: rect.left - crop.left,
|
|
725
|
+
y: crop.top - rect.top,
|
|
726
|
+
width: rect.right - rect.left,
|
|
727
|
+
height: rect.top - rect.bottom
|
|
728
|
+
});
|
|
729
|
+
const sameAnnotationRef = (left, right) => {
|
|
730
|
+
if (left.kind !== right.kind || left.pageObjectNumber !== right.pageObjectNumber) return false;
|
|
731
|
+
if (left.kind === "objectNumber" && right.kind === "objectNumber") return left.annotObjectNumber === right.annotObjectNumber;
|
|
732
|
+
if (left.kind === "nm" && right.kind === "nm") return left.nm === right.nm;
|
|
733
|
+
return left.kind === "index" && right.kind === "index" && left.index === right.index && left.revision === right.revision;
|
|
650
734
|
};
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
735
|
+
/**
|
|
736
|
+
* The form shell. Pure `update` runs here; the resulting model is dispatched
|
|
737
|
+
* to the store; engine calls happen around it. Every read the frameworks do
|
|
738
|
+
* goes through memoized projections keyed on `model.seq`.
|
|
739
|
+
*/
|
|
740
|
+
function createFormCapability(ctx, options = {}) {
|
|
741
|
+
const model = () => ctx.getState().model;
|
|
742
|
+
const apply = (msg) => {
|
|
743
|
+
ctx.dispatch({
|
|
744
|
+
type: "SET_MODEL",
|
|
745
|
+
model: update(model(), msg)
|
|
746
|
+
});
|
|
747
|
+
};
|
|
748
|
+
const refKeyOf = (key) => {
|
|
749
|
+
if (key.startsWith("obj:")) return {
|
|
750
|
+
kind: "objectNumber",
|
|
751
|
+
fieldObjectNumber: Number(key.slice(4))
|
|
752
|
+
};
|
|
753
|
+
return {
|
|
754
|
+
kind: "fqn",
|
|
755
|
+
name: key.slice(4)
|
|
756
|
+
};
|
|
757
|
+
};
|
|
758
|
+
const enqueueMutation = createSerialMutationQueue();
|
|
759
|
+
const scripting = options.scripting?.enabled && ctx.doc ? createFormScriptingController({
|
|
760
|
+
doc: ctx.doc,
|
|
761
|
+
document: () => ctx.document(),
|
|
762
|
+
config: options.scripting,
|
|
763
|
+
sandboxFactory: options.scripting.sandboxFactory ?? (() => import("@embedpdf/core-js-sandbox").then(({ createQuickJsSandbox }) => createQuickJsSandbox()))
|
|
764
|
+
}) : null;
|
|
765
|
+
if (scripting) ctx.cleanup(() => scripting.dispose());
|
|
766
|
+
let uiEffectProvider = null;
|
|
767
|
+
const surfaceScriptingResult = (result) => {
|
|
768
|
+
const observe = (callback) => {
|
|
769
|
+
if (!callback) return;
|
|
770
|
+
try {
|
|
771
|
+
callback();
|
|
772
|
+
} catch (error) {
|
|
773
|
+
globalThis.console?.error("[form] scripting observer failed:", error);
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
for (const effect of result.uiEffects) {
|
|
777
|
+
observe(options.scripting?.onUiEffect ? () => options.scripting.onUiEffect(effect) : void 0);
|
|
778
|
+
observe(uiEffectProvider ? () => uiEffectProvider(effect) : void 0);
|
|
779
|
+
}
|
|
780
|
+
for (const diagnostic of result.diagnostics) observe(options.scripting?.onDiagnostic ? () => options.scripting.onDiagnostic(diagnostic) : void 0);
|
|
781
|
+
if (result.error) observe(options.scripting?.onError ? () => options.scripting.onError(result.error) : void 0);
|
|
782
|
+
};
|
|
783
|
+
let refreshPromise = null;
|
|
784
|
+
const refresh = async (force = false) => {
|
|
785
|
+
const doc = ctx.doc;
|
|
786
|
+
if (!doc) return;
|
|
787
|
+
if (refreshPromise) {
|
|
788
|
+
await refreshPromise;
|
|
789
|
+
if (!force) return;
|
|
790
|
+
}
|
|
791
|
+
refreshPromise = doc.forms.list().then((snapshot) => apply({
|
|
792
|
+
t: "snapshot",
|
|
793
|
+
snapshot
|
|
794
|
+
})).finally(() => {
|
|
795
|
+
refreshPromise = null;
|
|
796
|
+
});
|
|
797
|
+
return refreshPromise;
|
|
798
|
+
};
|
|
799
|
+
const geomLoading = /* @__PURE__ */ new Set();
|
|
800
|
+
const ensureGeom = (pon) => {
|
|
801
|
+
const doc = ctx.doc;
|
|
802
|
+
if (!doc || geomLoading.has(pon) || model().geom[pon]) return;
|
|
803
|
+
const crop = ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop;
|
|
804
|
+
if (!crop) return;
|
|
805
|
+
geomLoading.add(pon);
|
|
806
|
+
doc.page(pon).annotations.list().then(({ annotations }) => {
|
|
807
|
+
const boxes = {};
|
|
808
|
+
for (const dto of annotations) {
|
|
809
|
+
if (dto.subtype !== "widget") continue;
|
|
810
|
+
const objectNumber = dto.ref.kind === "objectNumber" ? dto.ref.annotObjectNumber : 0;
|
|
811
|
+
if (objectNumber > 0) boxes[objectNumber] = toBox(dto.rect, crop);
|
|
812
|
+
}
|
|
813
|
+
apply({
|
|
814
|
+
t: "pageGeom",
|
|
815
|
+
pageObjectNumber: pon,
|
|
816
|
+
boxes
|
|
817
|
+
});
|
|
818
|
+
}).finally(() => {
|
|
819
|
+
geomLoading.delete(pon);
|
|
820
|
+
});
|
|
821
|
+
};
|
|
822
|
+
const fillCache = /* @__PURE__ */ new Map();
|
|
823
|
+
const fillItems$1 = (pon) => {
|
|
824
|
+
const m = model();
|
|
825
|
+
const hit = fillCache.get(pon);
|
|
826
|
+
if (hit && hit.seq === m.seq) return hit.items;
|
|
827
|
+
const items = fillItems(m, pon);
|
|
828
|
+
fillCache.set(pon, {
|
|
829
|
+
seq: m.seq,
|
|
830
|
+
items
|
|
831
|
+
});
|
|
832
|
+
return items;
|
|
833
|
+
};
|
|
834
|
+
const fillItemCache = /* @__PURE__ */ new Map();
|
|
835
|
+
const fillItem = (annotObjectNumber) => {
|
|
836
|
+
const m = model();
|
|
837
|
+
const hit = fillItemCache.get(annotObjectNumber);
|
|
838
|
+
if (hit && hit.seq === m.seq) return hit.item;
|
|
839
|
+
const item = fillItemForWidget(m, annotObjectNumber);
|
|
840
|
+
fillItemCache.set(annotObjectNumber, {
|
|
841
|
+
seq: m.seq,
|
|
842
|
+
item
|
|
843
|
+
});
|
|
844
|
+
return item;
|
|
845
|
+
};
|
|
846
|
+
const commitValueNow = async (ref, value) => {
|
|
847
|
+
const doc = ctx.doc;
|
|
848
|
+
if (!doc) throw new Error("no document");
|
|
849
|
+
if (scripting) {
|
|
850
|
+
const result = await scripting.commit(await doc.forms.list(), ref, value);
|
|
851
|
+
surfaceScriptingResult(result);
|
|
852
|
+
if (result.effectsResult !== null) await refresh(true);
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
855
|
+
const result = await doc.forms.setValue(ref, value);
|
|
856
|
+
await refresh(true);
|
|
857
|
+
return {
|
|
858
|
+
status: result.changedWidgets.length > 0 ? "applied" : "unchanged",
|
|
859
|
+
scripted: false,
|
|
860
|
+
effectsResult: null,
|
|
861
|
+
uiEffects: [],
|
|
862
|
+
diagnostics: []
|
|
863
|
+
};
|
|
864
|
+
};
|
|
865
|
+
const write = (key, value) => enqueueMutation(async () => {
|
|
866
|
+
if (!ctx.doc) return;
|
|
867
|
+
apply({
|
|
868
|
+
t: "writeStart",
|
|
869
|
+
key
|
|
870
|
+
});
|
|
871
|
+
try {
|
|
872
|
+
const result = await commitValueNow(refKeyOf(key), value);
|
|
873
|
+
if (result.status === "rejected" || result.status === "failed") apply({
|
|
874
|
+
t: "writeFailed",
|
|
875
|
+
key
|
|
876
|
+
});
|
|
877
|
+
else if (result.effectsResult === null && result.scripted) apply({
|
|
878
|
+
t: "writeFailed",
|
|
879
|
+
key
|
|
880
|
+
});
|
|
881
|
+
} catch (err) {
|
|
882
|
+
apply({
|
|
883
|
+
t: "writeFailed",
|
|
884
|
+
key
|
|
885
|
+
});
|
|
886
|
+
throw err;
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
const can = (cap) => ctx.doc?.security.allows(cap) ?? false;
|
|
890
|
+
const annotationHost = ctx.tryGet(AnnotationToken$1);
|
|
891
|
+
const annotationActivation = async (ref) => {
|
|
892
|
+
const doc = ctx.doc;
|
|
893
|
+
if (!doc) return null;
|
|
894
|
+
const loaded = annotationHost?.get(ref);
|
|
895
|
+
if (loaded?.subtype === "widget") return loaded.actions?.activate ?? null;
|
|
896
|
+
const { annotations } = await doc.page(ref.pageObjectNumber).annotations.list();
|
|
897
|
+
const annotation = annotations.find((candidate) => sameAnnotationRef(candidate.ref, ref));
|
|
898
|
+
return annotation?.subtype === "widget" ? annotation.actions?.activate ?? null : null;
|
|
899
|
+
};
|
|
900
|
+
const activateWidgetNow = async (key, annotationRef) => {
|
|
901
|
+
const doc = ctx.doc;
|
|
902
|
+
if (!doc) throw new Error("no document");
|
|
903
|
+
if (!scripting) return {
|
|
904
|
+
status: "unchanged",
|
|
905
|
+
scripted: false,
|
|
906
|
+
effectsResult: null,
|
|
907
|
+
uiEffects: [],
|
|
908
|
+
diagnostics: []
|
|
909
|
+
};
|
|
910
|
+
const action = await annotationActivation(annotationRef);
|
|
911
|
+
if (!action) return {
|
|
912
|
+
status: "unchanged",
|
|
913
|
+
scripted: true,
|
|
914
|
+
effectsResult: null,
|
|
915
|
+
uiEffects: [],
|
|
916
|
+
diagnostics: []
|
|
917
|
+
};
|
|
918
|
+
const result = await scripting.activate(await doc.forms.list(), refKeyOf(key), action);
|
|
919
|
+
surfaceScriptingResult(result);
|
|
920
|
+
if (result.effectsResult !== null) await refresh(true);
|
|
921
|
+
return result;
|
|
922
|
+
};
|
|
923
|
+
const nudgeAnnotations = (pons) => {
|
|
924
|
+
if (!annotationHost) return;
|
|
925
|
+
for (const pon of new Set(pons)) annotationHost.reloadPage(pon);
|
|
926
|
+
};
|
|
927
|
+
/** Content-space box → PDF rect (inverse of `toBox`). */
|
|
928
|
+
const toPdfRect = (box, crop) => ({
|
|
929
|
+
left: box.x + crop.left,
|
|
930
|
+
top: crop.top - box.y,
|
|
931
|
+
right: box.x + crop.left + box.width,
|
|
932
|
+
bottom: crop.top - box.y - box.height
|
|
933
|
+
});
|
|
934
|
+
/** The page's content box (`{0,0,w,h}`), for page-bound placement math. */
|
|
935
|
+
const pageBox = (pon) => {
|
|
936
|
+
const crop = ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop;
|
|
937
|
+
return crop ? {
|
|
938
|
+
x: 0,
|
|
939
|
+
y: 0,
|
|
940
|
+
width: crop.right - crop.left,
|
|
941
|
+
height: crop.top - crop.bottom
|
|
942
|
+
} : null;
|
|
943
|
+
};
|
|
944
|
+
/** Deterministic, collision-free auto-name: `text_1`, `text_2`, … counted
|
|
945
|
+
* against the CURRENT snapshot (rename in the field panel). */
|
|
946
|
+
const autoName = (family) => {
|
|
947
|
+
const names = new Set((model().snapshot?.fields ?? []).map((f) => f.name));
|
|
948
|
+
let n = 1;
|
|
949
|
+
while (names.has(`${family}_${n}`)) n++;
|
|
950
|
+
return `${family}_${n}`;
|
|
951
|
+
};
|
|
952
|
+
const placeFieldNow = async (input) => {
|
|
953
|
+
const doc = ctx.doc;
|
|
954
|
+
const pon = input.pageObjectNumber;
|
|
955
|
+
const crop = ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop;
|
|
956
|
+
if (!doc || !crop) throw new Error("[form] placeField: document/page not ready");
|
|
957
|
+
const page = pageBox(pon);
|
|
958
|
+
const x = Math.max(page.x, Math.min(input.box.x, page.width));
|
|
959
|
+
const y = Math.max(page.y, Math.min(input.box.y, page.height));
|
|
960
|
+
const box = {
|
|
961
|
+
x,
|
|
962
|
+
y,
|
|
963
|
+
width: Math.max(0, Math.min(input.box.x + input.box.width, page.width) - x),
|
|
964
|
+
height: Math.max(0, Math.min(input.box.y + input.box.height, page.height) - y)
|
|
965
|
+
};
|
|
966
|
+
if (box.width < 1 || box.height < 1) throw new Error("[form] placeField: degenerate box (size the box before placing)");
|
|
967
|
+
const { family, appearance } = input;
|
|
968
|
+
const name = autoName(family);
|
|
969
|
+
const placement = {
|
|
970
|
+
pageObjectNumber: pon,
|
|
971
|
+
rect: toPdfRect(box, crop),
|
|
972
|
+
...appearance ? { appearance } : {}
|
|
973
|
+
};
|
|
974
|
+
const draft = family === "radio" ? {
|
|
975
|
+
family,
|
|
976
|
+
name,
|
|
977
|
+
widgets: [{
|
|
978
|
+
...placement,
|
|
979
|
+
onState: "option1"
|
|
980
|
+
}]
|
|
981
|
+
} : family === "combobox" || family === "listbox" ? {
|
|
982
|
+
family,
|
|
983
|
+
name,
|
|
984
|
+
widget: placement,
|
|
985
|
+
options: [{
|
|
986
|
+
label: "Option 1",
|
|
987
|
+
value: "Option 1"
|
|
988
|
+
}, {
|
|
989
|
+
label: "Option 2",
|
|
990
|
+
value: "Option 2"
|
|
991
|
+
}]
|
|
992
|
+
} : {
|
|
993
|
+
family,
|
|
994
|
+
name,
|
|
995
|
+
widget: placement
|
|
996
|
+
};
|
|
997
|
+
const result = await doc.forms.createField(draft);
|
|
998
|
+
await refresh(true);
|
|
999
|
+
apply({
|
|
1000
|
+
t: "clearGeom",
|
|
1001
|
+
pageObjectNumber: pon
|
|
1002
|
+
});
|
|
1003
|
+
if (annotationHost) await annotationHost.reloadPage(pon);
|
|
1004
|
+
const widget = result.field.widgets.find((w) => w.pageObjectNumber === pon) ?? null;
|
|
1005
|
+
return {
|
|
1006
|
+
field: result.field,
|
|
1007
|
+
widget
|
|
1008
|
+
};
|
|
1009
|
+
};
|
|
1010
|
+
const updateFieldNow = async (key, patch) => {
|
|
1011
|
+
const doc = ctx.doc;
|
|
1012
|
+
if (!doc) return;
|
|
1013
|
+
await doc.forms.updateField(refKeyOf(key), patch);
|
|
1014
|
+
await refresh(true);
|
|
1015
|
+
};
|
|
1016
|
+
const deleteFieldNow = async (key) => {
|
|
1017
|
+
const doc = ctx.doc;
|
|
1018
|
+
if (!doc) return;
|
|
1019
|
+
const pons = fieldByKey(model(), key)?.widgets.map((w) => w.pageObjectNumber).filter((p) => p > 0) ?? [];
|
|
1020
|
+
await doc.forms.deleteField(refKeyOf(key));
|
|
1021
|
+
await refresh(true);
|
|
1022
|
+
for (const pon of new Set(pons)) apply({
|
|
1023
|
+
t: "clearGeom",
|
|
1024
|
+
pageObjectNumber: pon
|
|
1025
|
+
});
|
|
1026
|
+
nudgeAnnotations(pons);
|
|
1027
|
+
};
|
|
1028
|
+
const detachWidgetNow = async (key, annotObjectNumber) => {
|
|
1029
|
+
const doc = ctx.doc;
|
|
1030
|
+
if (!doc) return;
|
|
1031
|
+
const widget = fieldByKey(model(), key)?.widgets.find((w) => w.annotObjectNumber === annotObjectNumber);
|
|
1032
|
+
await doc.forms.detachWidget(refKeyOf(key), {
|
|
1033
|
+
annotObjectNumber,
|
|
1034
|
+
pageObjectNumber: widget?.pageObjectNumber ?? 0
|
|
1035
|
+
});
|
|
1036
|
+
await refresh(true);
|
|
1037
|
+
if (widget && widget.pageObjectNumber > 0) {
|
|
1038
|
+
apply({
|
|
1039
|
+
t: "clearGeom",
|
|
1040
|
+
pageObjectNumber: widget.pageObjectNumber
|
|
1041
|
+
});
|
|
1042
|
+
nudgeAnnotations([widget.pageObjectNumber]);
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
refresh();
|
|
1046
|
+
return {
|
|
1047
|
+
snapshot: () => model().snapshot,
|
|
1048
|
+
refresh,
|
|
1049
|
+
fillItems: fillItems$1,
|
|
1050
|
+
fillItem,
|
|
1051
|
+
ensureGeom,
|
|
1052
|
+
field: (key) => fieldByKey(model(), key),
|
|
1053
|
+
fieldForWidget: (annotObjectNumber) => fieldForWidget(model(), annotObjectNumber),
|
|
1054
|
+
setText: (key, value) => write(key, {
|
|
1055
|
+
type: "text",
|
|
1056
|
+
value
|
|
1057
|
+
}),
|
|
1058
|
+
toggle: (key, onState) => write(key, {
|
|
1059
|
+
type: "toggle",
|
|
1060
|
+
state: onState
|
|
1061
|
+
}),
|
|
1062
|
+
choose: (key, values) => write(key, {
|
|
1063
|
+
type: "choice",
|
|
1064
|
+
values
|
|
1065
|
+
}),
|
|
1066
|
+
reset: (key) => enqueueMutation(async () => {
|
|
1067
|
+
const doc = ctx.doc;
|
|
1068
|
+
if (!doc) return;
|
|
1069
|
+
apply({
|
|
1070
|
+
t: "writeStart",
|
|
1071
|
+
key
|
|
1072
|
+
});
|
|
1073
|
+
try {
|
|
1074
|
+
const result = await doc.forms.reset(refKeyOf(key));
|
|
1075
|
+
apply({
|
|
1076
|
+
t: "writeDone",
|
|
1077
|
+
key,
|
|
1078
|
+
field: result.field
|
|
1079
|
+
});
|
|
1080
|
+
} catch (err) {
|
|
1081
|
+
apply({
|
|
1082
|
+
t: "writeFailed",
|
|
1083
|
+
key
|
|
1084
|
+
});
|
|
1085
|
+
throw err;
|
|
1086
|
+
}
|
|
1087
|
+
}),
|
|
1088
|
+
commitValue: (ref, value) => enqueueMutation(() => commitValueNow(ref, value)),
|
|
1089
|
+
activateWidget: (key, annotationRef) => enqueueMutation(() => activateWidgetNow(key, annotationRef)),
|
|
1090
|
+
setUiEffectProvider: (provider) => {
|
|
1091
|
+
uiEffectProvider = provider;
|
|
1092
|
+
},
|
|
1093
|
+
setValue: (ref, value) => enqueueMutation(async () => {
|
|
1094
|
+
const doc = ctx.doc;
|
|
1095
|
+
if (!doc) throw new Error("no document");
|
|
1096
|
+
const result = await doc.forms.setValue(ref, value);
|
|
1097
|
+
await refresh(true);
|
|
1098
|
+
return result;
|
|
1099
|
+
}),
|
|
1100
|
+
exportData: async (format = "xfdf") => {
|
|
1101
|
+
const doc = ctx.doc;
|
|
1102
|
+
if (!doc) throw new Error("no document");
|
|
1103
|
+
return doc.forms.exportData(format);
|
|
1104
|
+
},
|
|
1105
|
+
importData: (data, format) => enqueueMutation(async () => {
|
|
1106
|
+
const doc = ctx.doc;
|
|
1107
|
+
if (!doc) throw new Error("no document");
|
|
1108
|
+
const result = await doc.forms.importData(data, format);
|
|
1109
|
+
apply({
|
|
1110
|
+
t: "snapshot",
|
|
1111
|
+
snapshot: result.snapshot
|
|
1112
|
+
});
|
|
1113
|
+
return result;
|
|
1114
|
+
}),
|
|
1115
|
+
repair: (repairOptions) => enqueueMutation(async () => {
|
|
1116
|
+
const doc = ctx.doc;
|
|
1117
|
+
if (!doc) throw new Error("no document");
|
|
1118
|
+
const result = await doc.forms.repair(repairOptions);
|
|
1119
|
+
await refresh(true);
|
|
1120
|
+
return result;
|
|
1121
|
+
}),
|
|
1122
|
+
placeField: (input) => enqueueMutation(() => placeFieldNow(input)),
|
|
1123
|
+
pageBox,
|
|
1124
|
+
updateField: (key, patch) => enqueueMutation(() => updateFieldNow(key, patch)),
|
|
1125
|
+
deleteField: (key) => enqueueMutation(() => deleteFieldNow(key)),
|
|
1126
|
+
detachWidget: (key, annotObjectNumber) => enqueueMutation(() => detachWidgetNow(key, annotObjectNumber)),
|
|
1127
|
+
canFill: () => can("doc.forms.fill"),
|
|
1128
|
+
canModify: () => can("doc.forms.modify")
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
//#endregion
|
|
1132
|
+
//#region src/effects.ts
|
|
1133
|
+
const STRUCTURAL = /* @__PURE__ */ new Set([
|
|
1134
|
+
"form.fieldCreated",
|
|
1135
|
+
"form.fieldUpdated",
|
|
1136
|
+
"form.fieldDeleted",
|
|
1137
|
+
"form.widgetAttached",
|
|
1138
|
+
"form.widgetDetached",
|
|
1139
|
+
"form.repaired"
|
|
1140
|
+
]);
|
|
1141
|
+
function registerFormEffects(ctx) {
|
|
1142
|
+
const doc = ctx.doc;
|
|
1143
|
+
if (!doc) return;
|
|
1144
|
+
let refreshing = false;
|
|
1145
|
+
const refresh = async () => {
|
|
1146
|
+
if (refreshing) return;
|
|
1147
|
+
refreshing = true;
|
|
1148
|
+
try {
|
|
1149
|
+
const snapshot = await doc.forms.list();
|
|
1150
|
+
ctx.dispatch({
|
|
1151
|
+
type: "SET_MODEL",
|
|
1152
|
+
model: update(ctx.getState().model, {
|
|
1153
|
+
t: "snapshot",
|
|
1154
|
+
snapshot
|
|
1155
|
+
})
|
|
1156
|
+
});
|
|
1157
|
+
} finally {
|
|
1158
|
+
refreshing = false;
|
|
1159
|
+
}
|
|
1160
|
+
};
|
|
1161
|
+
const unsubscribe = doc.events.subscribe((event) => {
|
|
1162
|
+
if (!event.type.startsWith("form.")) return;
|
|
1163
|
+
const structural = STRUCTURAL.has(event.type);
|
|
1164
|
+
if (event.origin.kind !== "remote" && !structural) return;
|
|
1165
|
+
if (structural) ctx.dispatch({
|
|
1166
|
+
type: "SET_MODEL",
|
|
1167
|
+
model: update(ctx.getState().model, { t: "clearGeom" })
|
|
1168
|
+
});
|
|
1169
|
+
refresh();
|
|
1170
|
+
});
|
|
1171
|
+
ctx.cleanup(unsubscribe);
|
|
1172
|
+
}
|
|
1173
|
+
//#endregion
|
|
1174
|
+
//#region src/tools.ts
|
|
1175
|
+
/** Palette tools keep widgets editable right after placement. */
|
|
1176
|
+
const PLACE_TAGS = ["form-place", "annotation-edit"];
|
|
1177
|
+
const FIELD_CHROME = {
|
|
1178
|
+
interiorColor: "#ffffff",
|
|
1179
|
+
color: "#6b7280",
|
|
1180
|
+
strokeWidth: 1
|
|
795
1181
|
};
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
name: "Checkbox",
|
|
871
|
-
alternateName: "Checkbox",
|
|
872
|
-
value: "Off",
|
|
873
|
-
type: PDF_FORM_FIELD_TYPE.CHECKBOX
|
|
874
|
-
}
|
|
875
|
-
},
|
|
876
|
-
behavior: {
|
|
877
|
-
useAppearanceStream: false
|
|
878
|
-
},
|
|
879
|
-
clickBehavior: {
|
|
880
|
-
enabled: true,
|
|
881
|
-
defaultSize: { width: 20, height: 20 }
|
|
882
|
-
},
|
|
883
|
-
pointerHandler: checkboxHandlerFactory
|
|
884
|
-
});
|
|
885
|
-
const formRadioButtonTool = defineAnnotationTool({
|
|
886
|
-
id: "formRadioButton",
|
|
887
|
-
name: "Radio Button",
|
|
888
|
-
labelKey: "form.radiobutton",
|
|
889
|
-
categories: ["annotation", "form"],
|
|
890
|
-
matchScore: (a) => {
|
|
891
|
-
var _a;
|
|
892
|
-
if (a.type !== PdfAnnotationSubtype.WIDGET) return 0;
|
|
893
|
-
const widget = a;
|
|
894
|
-
return ((_a = widget.field) == null ? void 0 : _a.type) === PDF_FORM_FIELD_TYPE.RADIOBUTTON ? 10 : 0;
|
|
895
|
-
},
|
|
896
|
-
interaction: {
|
|
897
|
-
exclusive: false,
|
|
898
|
-
cursor: "crosshair",
|
|
899
|
-
isDraggable: true,
|
|
900
|
-
isResizable: true,
|
|
901
|
-
isRotatable: false,
|
|
902
|
-
lockAspectRatio: true,
|
|
903
|
-
isGroupDraggable: true,
|
|
904
|
-
isGroupResizable: false,
|
|
905
|
-
isGroupRotatable: false
|
|
906
|
-
},
|
|
907
|
-
defaults: {
|
|
908
|
-
type: PdfAnnotationSubtype.WIDGET,
|
|
909
|
-
strokeColor: "#000000",
|
|
910
|
-
color: "#FFFFFF",
|
|
911
|
-
strokeWidth: 1,
|
|
912
|
-
field: {
|
|
913
|
-
flag: PDF_FORM_FIELD_FLAG.BUTTON_RADIO | PDF_FORM_FIELD_FLAG.BUTTON_NOTOGGLETOOFF,
|
|
914
|
-
name: "RadioButton",
|
|
915
|
-
alternateName: "RadioButton",
|
|
916
|
-
value: "Off",
|
|
917
|
-
type: PDF_FORM_FIELD_TYPE.RADIOBUTTON,
|
|
918
|
-
options: []
|
|
919
|
-
}
|
|
920
|
-
},
|
|
921
|
-
behavior: {
|
|
922
|
-
useAppearanceStream: false
|
|
923
|
-
},
|
|
924
|
-
clickBehavior: {
|
|
925
|
-
enabled: true,
|
|
926
|
-
defaultSize: { width: 20, height: 20 }
|
|
927
|
-
},
|
|
928
|
-
pointerHandler: radioButtonHandlerFactory
|
|
929
|
-
});
|
|
930
|
-
const formComboboxTool = defineAnnotationTool({
|
|
931
|
-
id: "formCombobox",
|
|
932
|
-
name: "Combo Box",
|
|
933
|
-
labelKey: "form.combobox",
|
|
934
|
-
categories: ["annotation", "form"],
|
|
935
|
-
matchScore: (a) => {
|
|
936
|
-
var _a;
|
|
937
|
-
if (a.type !== PdfAnnotationSubtype.WIDGET) return 0;
|
|
938
|
-
const widget = a;
|
|
939
|
-
return ((_a = widget.field) == null ? void 0 : _a.type) === PDF_FORM_FIELD_TYPE.COMBOBOX ? 10 : 0;
|
|
940
|
-
},
|
|
941
|
-
interaction: {
|
|
942
|
-
exclusive: false,
|
|
943
|
-
cursor: "crosshair",
|
|
944
|
-
isDraggable: true,
|
|
945
|
-
isResizable: true,
|
|
946
|
-
isRotatable: false,
|
|
947
|
-
isGroupDraggable: true,
|
|
948
|
-
isGroupResizable: false,
|
|
949
|
-
isGroupRotatable: false
|
|
950
|
-
},
|
|
951
|
-
defaults: {
|
|
952
|
-
type: PdfAnnotationSubtype.WIDGET,
|
|
953
|
-
fontFamily: PdfStandardFont.Helvetica,
|
|
954
|
-
fontSize: 12,
|
|
955
|
-
fontColor: "#000000",
|
|
956
|
-
strokeColor: "#000000",
|
|
957
|
-
color: "#FFFFFF",
|
|
958
|
-
strokeWidth: 1,
|
|
959
|
-
field: {
|
|
960
|
-
flag: PDF_FORM_FIELD_FLAG.NONE,
|
|
961
|
-
name: "ComboBox",
|
|
962
|
-
alternateName: "ComboBox",
|
|
963
|
-
value: "",
|
|
964
|
-
type: PDF_FORM_FIELD_TYPE.COMBOBOX,
|
|
965
|
-
options: [
|
|
966
|
-
{ label: "Option 1", isSelected: true },
|
|
967
|
-
{ label: "Option 2", isSelected: false },
|
|
968
|
-
{ label: "Option 3", isSelected: false }
|
|
969
|
-
]
|
|
970
|
-
}
|
|
971
|
-
},
|
|
972
|
-
behavior: {
|
|
973
|
-
useAppearanceStream: false
|
|
974
|
-
},
|
|
975
|
-
clickBehavior: {
|
|
976
|
-
enabled: true,
|
|
977
|
-
defaultSize: { width: 200, height: 40 }
|
|
978
|
-
},
|
|
979
|
-
pointerHandler: comboboxHandlerFactory
|
|
1182
|
+
const FORM_TOOLS = [
|
|
1183
|
+
{
|
|
1184
|
+
id: "form-text",
|
|
1185
|
+
family: "text",
|
|
1186
|
+
visualKind: "widget-text",
|
|
1187
|
+
clickCreate: {
|
|
1188
|
+
width: 160,
|
|
1189
|
+
height: 24
|
|
1190
|
+
},
|
|
1191
|
+
defaults: {
|
|
1192
|
+
...FIELD_CHROME,
|
|
1193
|
+
fontSize: 12
|
|
1194
|
+
},
|
|
1195
|
+
cursor: "crosshair"
|
|
1196
|
+
},
|
|
1197
|
+
{
|
|
1198
|
+
id: "form-checkbox",
|
|
1199
|
+
family: "checkbox",
|
|
1200
|
+
visualKind: "widget-toggle",
|
|
1201
|
+
clickCreate: {
|
|
1202
|
+
width: 18,
|
|
1203
|
+
height: 18
|
|
1204
|
+
},
|
|
1205
|
+
defaults: FIELD_CHROME,
|
|
1206
|
+
cursor: "crosshair"
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
id: "form-radio",
|
|
1210
|
+
family: "radio",
|
|
1211
|
+
visualKind: "widget-toggle",
|
|
1212
|
+
clickCreate: {
|
|
1213
|
+
width: 18,
|
|
1214
|
+
height: 18
|
|
1215
|
+
},
|
|
1216
|
+
defaults: FIELD_CHROME,
|
|
1217
|
+
cursor: "crosshair"
|
|
1218
|
+
},
|
|
1219
|
+
{
|
|
1220
|
+
id: "form-combobox",
|
|
1221
|
+
family: "combobox",
|
|
1222
|
+
visualKind: "widget-choice",
|
|
1223
|
+
clickCreate: {
|
|
1224
|
+
width: 140,
|
|
1225
|
+
height: 24
|
|
1226
|
+
},
|
|
1227
|
+
defaults: {
|
|
1228
|
+
...FIELD_CHROME,
|
|
1229
|
+
fontSize: 12
|
|
1230
|
+
},
|
|
1231
|
+
cursor: "crosshair"
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
id: "form-listbox",
|
|
1235
|
+
family: "listbox",
|
|
1236
|
+
visualKind: "widget-choice",
|
|
1237
|
+
clickCreate: {
|
|
1238
|
+
width: 140,
|
|
1239
|
+
height: 72
|
|
1240
|
+
},
|
|
1241
|
+
defaults: {
|
|
1242
|
+
...FIELD_CHROME,
|
|
1243
|
+
fontSize: 12
|
|
1244
|
+
},
|
|
1245
|
+
cursor: "crosshair"
|
|
1246
|
+
}
|
|
1247
|
+
];
|
|
1248
|
+
const FORM_TOOL_BY_ID = new Map(FORM_TOOLS.map((t) => [t.id, t]));
|
|
1249
|
+
//#endregion
|
|
1250
|
+
//#region src/handler.ts
|
|
1251
|
+
const rectFrom = (a, b) => ({
|
|
1252
|
+
x: Math.min(a.x, b.x),
|
|
1253
|
+
y: Math.min(a.y, b.y),
|
|
1254
|
+
width: Math.abs(b.x - a.x),
|
|
1255
|
+
height: Math.abs(b.y - a.y)
|
|
980
1256
|
});
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1257
|
+
/**
|
|
1258
|
+
* Draw-to-place: drag a box (live placement preview through the annotation
|
|
1259
|
+
* plane's ghost pipeline) or just click (the tool's `clickCreate` policy,
|
|
1260
|
+
* resolved by the SAME `resolveClickPlacement` the annotation core uses) —
|
|
1261
|
+
* the commit creates field + widget through `doc.forms.placeField`, styled
|
|
1262
|
+
* from the tool's live defaults. The tool stays active for repeat placement
|
|
1263
|
+
* (v2 rubber-stamp feel) and the fresh widget is auto-selected.
|
|
1264
|
+
*
|
|
1265
|
+
* Gesture rules match the annotation handlers: page-anchored via the sample
|
|
1266
|
+
* projection (the box keeps sizing along the edge when the cursor overshoots
|
|
1267
|
+
* or crosses a gap), the UP sample is the final point, and a CLICK means
|
|
1268
|
+
* width AND height under the shared threshold — a thin 100×2 drag is a drag.
|
|
1269
|
+
*/
|
|
1270
|
+
function createPlaceHandler(form, interaction, annotation) {
|
|
1271
|
+
let origin = null;
|
|
1272
|
+
return {
|
|
1273
|
+
id: "form-place",
|
|
1274
|
+
priority: 95,
|
|
1275
|
+
enabledFor: (t) => t.enables.has("form-place"),
|
|
1276
|
+
onDown: (s) => {
|
|
1277
|
+
if (!s.page || !FORM_TOOL_BY_ID.has(interaction.activeToolId())) return false;
|
|
1278
|
+
if (!form.canModify()) return false;
|
|
1279
|
+
origin = {
|
|
1280
|
+
pon: s.page.pon,
|
|
1281
|
+
start: s.page.point,
|
|
1282
|
+
last: s.page.point
|
|
1283
|
+
};
|
|
1284
|
+
return true;
|
|
1285
|
+
},
|
|
1286
|
+
onMove: (s) => {
|
|
1287
|
+
if (!origin) return;
|
|
1288
|
+
const point = samplePointOn(s, origin.pon);
|
|
1289
|
+
if (!point) return;
|
|
1290
|
+
origin.last = point;
|
|
1291
|
+
const box = rectFrom(origin.start, point);
|
|
1292
|
+
if (annotation) if (Math.max(box.width, box.height) >= MIN_DRAG) annotation.setPlacementPreview(interaction.activeToolId(), origin.pon, box);
|
|
1293
|
+
else annotation.clearPlacementPreview();
|
|
1294
|
+
},
|
|
1295
|
+
onUp: (s) => {
|
|
1296
|
+
if (!origin) return;
|
|
1297
|
+
const o = origin;
|
|
1298
|
+
origin = null;
|
|
1299
|
+
annotation?.clearPlacementPreview();
|
|
1300
|
+
const toolId = interaction.activeToolId();
|
|
1301
|
+
const tool = FORM_TOOL_BY_ID.get(toolId);
|
|
1302
|
+
if (!tool) return;
|
|
1303
|
+
const end = samplePointOn(s, o.pon) ?? o.last;
|
|
1304
|
+
const dragged = rectFrom(o.start, end);
|
|
1305
|
+
const isClick = dragged.width < MIN_DRAG && dragged.height < MIN_DRAG;
|
|
1306
|
+
const pageBox = form.pageBox(o.pon);
|
|
1307
|
+
const box = isClick ? boxOfClick(o.start, tool.clickCreate, pageBox) : dragged;
|
|
1308
|
+
form.placeField({
|
|
1309
|
+
family: tool.family,
|
|
1310
|
+
pageObjectNumber: o.pon,
|
|
1311
|
+
box,
|
|
1312
|
+
appearance: widgetAppearanceFromProps(annotation ? annotation.currentDefaults(toolId) : tool.defaults)
|
|
1313
|
+
}).then((placed) => {
|
|
1314
|
+
if (!annotation || interaction.activeToolId() !== toolId) return;
|
|
1315
|
+
const widget = placed.widget;
|
|
1316
|
+
if (!widget || widget.annotObjectNumber <= 0) return;
|
|
1317
|
+
annotation.select({
|
|
1318
|
+
kind: "objectNumber",
|
|
1319
|
+
annotObjectNumber: widget.annotObjectNumber,
|
|
1320
|
+
pageObjectNumber: o.pon
|
|
1321
|
+
});
|
|
1322
|
+
}).catch((err) => {
|
|
1323
|
+
globalThis.console?.error("[form] placeField failed:", err);
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
/** The click-create box through the SHARED placement layer (fields are boxes;
|
|
1329
|
+
* the policy anchor + page clamp are exactly the annotation click's). */
|
|
1330
|
+
function boxOfClick(point, policy, pageBox) {
|
|
1331
|
+
const placement = resolveClickPlacement(point, policy, { pageBox: pageBox ?? void 0 });
|
|
1332
|
+
return placement.kind === "box" ? placement.rect : {
|
|
1333
|
+
x: point.x,
|
|
1334
|
+
y: point.y,
|
|
1335
|
+
width: policy.width,
|
|
1336
|
+
height: policy.height
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
//#endregion
|
|
1340
|
+
//#region src/reducer.ts
|
|
1341
|
+
const initialFormState = () => ({ model: initialModel() });
|
|
1342
|
+
/** Dumb store: the pure core computes the next model; the shell dispatches it. */
|
|
1343
|
+
const formReducer = (state, action) => action.type === "SET_MODEL" ? { model: action.model } : state;
|
|
1344
|
+
//#endregion
|
|
1345
|
+
//#region src/types.ts
|
|
1346
|
+
const FormToken = createCapabilityToken("form");
|
|
1347
|
+
//#endregion
|
|
1348
|
+
//#region src/form.plugin.ts
|
|
1349
|
+
/**
|
|
1350
|
+
* The form plugin: the FIELD plane. Document-scoped; requires the
|
|
1351
|
+
* interaction hub. Fill mode works with no annotation plugin at all —
|
|
1352
|
+
* geometry is read from the engine's widget DTOs. When the annotation
|
|
1353
|
+
* plugin IS present, a Behavior keeps widgets geometry-inert while the
|
|
1354
|
+
* active tool carries 'form-fill' (the built-in pointer/pan tools do, so
|
|
1355
|
+
* filling is the resting state) and stands the fill controls down under
|
|
1356
|
+
* every other tool — the single-active-tool hub IS the mode switch.
|
|
1357
|
+
* Design mode = the 'form-edit' / palette tools: no 'form-fill' tag, so
|
|
1358
|
+
* widgets become ordinary editable annotations.
|
|
1359
|
+
*/
|
|
1360
|
+
const formPlugin = (options = {}) => definePlugin({
|
|
1361
|
+
id: "form",
|
|
1362
|
+
token: FormToken,
|
|
1363
|
+
scope: "document",
|
|
1364
|
+
requires: [InteractionToken],
|
|
1365
|
+
optional: [AnnotationToken],
|
|
1366
|
+
initialState: initialFormState,
|
|
1367
|
+
reduce: formReducer,
|
|
1368
|
+
capability: (ctx) => createFormCapability(ctx, options),
|
|
1369
|
+
effects: registerFormEffects,
|
|
1370
|
+
init: (ctx) => {
|
|
1371
|
+
const interaction = ctx.get(InteractionToken);
|
|
1372
|
+
const form = ctx.get(FormToken);
|
|
1373
|
+
interaction.registerTool({
|
|
1374
|
+
id: "form-fill",
|
|
1375
|
+
cursor: "default",
|
|
1376
|
+
enables: /* @__PURE__ */ new Set(["form-fill", "link-nav"])
|
|
1377
|
+
});
|
|
1378
|
+
interaction.registerTool({
|
|
1379
|
+
id: "form-edit",
|
|
1380
|
+
cursor: "default",
|
|
1381
|
+
enables: /* @__PURE__ */ new Set(["annotation-edit", "annotation-marquee"])
|
|
1382
|
+
});
|
|
1383
|
+
const annotation = ctx.tryGet(AnnotationToken$1);
|
|
1384
|
+
for (const t of FORM_TOOLS) if (annotation) annotation.registerTool({
|
|
1385
|
+
id: t.id,
|
|
1386
|
+
subtype: t.visualKind,
|
|
1387
|
+
cursor: t.cursor,
|
|
1388
|
+
enables: [...PLACE_TAGS],
|
|
1389
|
+
clickCreate: t.clickCreate,
|
|
1390
|
+
defaults: t.defaults
|
|
1391
|
+
});
|
|
1392
|
+
else interaction.registerTool({
|
|
1393
|
+
id: t.id,
|
|
1394
|
+
cursor: t.cursor,
|
|
1395
|
+
enables: new Set(PLACE_TAGS)
|
|
1396
|
+
});
|
|
1397
|
+
interaction.registerHandler(createPlaceHandler(form, interaction, annotation));
|
|
1398
|
+
if (annotation) annotation.registerBehavior({
|
|
1399
|
+
id: "form-widgets",
|
|
1400
|
+
matches: (a) => a.subtype.startsWith("widget"),
|
|
1401
|
+
engaged: () => interaction.activeTool()?.enables.has("form-fill") ?? false
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1031
1404
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
formComboboxTool,
|
|
1037
|
-
formListboxTool
|
|
1038
|
-
];
|
|
1039
|
-
const _FormPlugin = class _FormPlugin extends BasePlugin {
|
|
1040
|
-
constructor(id, registry, _config) {
|
|
1041
|
-
var _a, _b;
|
|
1042
|
-
super(id, registry);
|
|
1043
|
-
this.FORM_HISTORY_TOPIC = "form-fields";
|
|
1044
|
-
this.annotation = null;
|
|
1045
|
-
this.history = null;
|
|
1046
|
-
this.state$ = createScopedEmitter(
|
|
1047
|
-
(documentId, state) => ({ documentId, state })
|
|
1048
|
-
);
|
|
1049
|
-
this.fieldValueChange$ = createScopedEmitter((_, event) => event, { cache: false });
|
|
1050
|
-
this.formReady$ = createScopedEmitter(
|
|
1051
|
-
(documentId, fields) => ({ documentId, fields }),
|
|
1052
|
-
{ cache: false }
|
|
1053
|
-
);
|
|
1054
|
-
this.fieldGroupIndex = /* @__PURE__ */ new Map();
|
|
1055
|
-
this.fieldNameIndex = /* @__PURE__ */ new Map();
|
|
1056
|
-
this.orderedFieldIndex = /* @__PURE__ */ new Map();
|
|
1057
|
-
this.propagationInProgress = /* @__PURE__ */ new Set();
|
|
1058
|
-
this.annotation = ((_a = registry.getPlugin("annotation")) == null ? void 0 : _a.provides()) ?? null;
|
|
1059
|
-
this.history = ((_b = registry.getPlugin(HistoryPlugin.id)) == null ? void 0 : _b.provides()) ?? null;
|
|
1060
|
-
if (this.annotation) {
|
|
1061
|
-
for (const tool of formTools) {
|
|
1062
|
-
this.annotation.addTool(tool);
|
|
1063
|
-
}
|
|
1064
|
-
this.annotation.onAnnotationEvent((event) => this.handleAnnotationEvent(event));
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
async initialize() {
|
|
1068
|
-
}
|
|
1069
|
-
onDocumentLoadingStarted(documentId) {
|
|
1070
|
-
this.dispatch(initFormState(documentId, { ...initialDocumentState }));
|
|
1071
|
-
}
|
|
1072
|
-
onDocumentClosed(documentId) {
|
|
1073
|
-
this.dispatch(cleanupFormState(documentId));
|
|
1074
|
-
this.fieldGroupIndex.delete(documentId);
|
|
1075
|
-
this.fieldNameIndex.delete(documentId);
|
|
1076
|
-
this.orderedFieldIndex.delete(documentId);
|
|
1077
|
-
this.state$.clearScope(documentId);
|
|
1078
|
-
this.fieldValueChange$.clearScope(documentId);
|
|
1079
|
-
this.formReady$.clearScope(documentId);
|
|
1080
|
-
}
|
|
1081
|
-
onStoreUpdated(prev, next) {
|
|
1082
|
-
for (const documentId in next.documents) {
|
|
1083
|
-
const prevDoc = prev.documents[documentId];
|
|
1084
|
-
const nextDoc = next.documents[documentId];
|
|
1085
|
-
if (prevDoc !== nextDoc) {
|
|
1086
|
-
this.state$.emit(documentId, nextDoc);
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
buildCapability() {
|
|
1091
|
-
return {
|
|
1092
|
-
getPageFormAnnoWidgets: (pageIndex, documentId) => this.getPageFormAnnoWidgets(pageIndex, documentId),
|
|
1093
|
-
setFormFieldValues: (pageIndex, annotation, newField, documentId) => this.setFormFieldValues(pageIndex, annotation, newField, documentId),
|
|
1094
|
-
renameField: (annotationId, name, documentId) => this.renameFieldMethod(annotationId, name, documentId),
|
|
1095
|
-
shareField: (annotationId, targetAnnotationId, documentId) => this.shareFieldMethod(annotationId, targetAnnotationId, documentId),
|
|
1096
|
-
renderWidget: (options, documentId) => this.renderWidget(options, documentId),
|
|
1097
|
-
selectField: (annotationId, documentId) => this.selectFieldMethod(annotationId, documentId),
|
|
1098
|
-
deselectField: (documentId) => this.deselectFieldMethod(documentId),
|
|
1099
|
-
getSelectedFieldId: (documentId) => this.getSelectedFieldId(documentId),
|
|
1100
|
-
selectNextField: (documentId) => this.selectNextFieldMethod(documentId),
|
|
1101
|
-
selectPreviousField: (documentId) => this.selectPreviousFieldMethod(documentId),
|
|
1102
|
-
activateField: (documentId) => this.activateFieldMethod(documentId),
|
|
1103
|
-
getState: (documentId) => this.getDocumentState(documentId),
|
|
1104
|
-
getFieldGroup: (annotationId, documentId) => this.getFieldGroup(annotationId, documentId),
|
|
1105
|
-
getFieldSiblings: (annotationId, documentId) => this.getFieldSiblings(annotationId, documentId),
|
|
1106
|
-
getFormValues: (documentId) => this.getFormValuesMethod(documentId),
|
|
1107
|
-
getFormFields: (documentId) => this.getFormFieldsMethod(documentId),
|
|
1108
|
-
setFormValues: (values, documentId) => this.setFormValuesMethod(values, documentId),
|
|
1109
|
-
forDocument: (documentId) => this.createFormScope(documentId),
|
|
1110
|
-
onStateChange: this.state$.onGlobal,
|
|
1111
|
-
onFieldValueChange: this.fieldValueChange$.onGlobal,
|
|
1112
|
-
onFormReady: this.formReady$.onGlobal
|
|
1113
|
-
};
|
|
1114
|
-
}
|
|
1115
|
-
createFormScope(documentId) {
|
|
1116
|
-
return {
|
|
1117
|
-
getPageFormAnnoWidgets: (pageIndex) => this.getPageFormAnnoWidgets(pageIndex, documentId),
|
|
1118
|
-
setFormFieldValues: (pageIndex, annotation, newField) => this.setFormFieldValues(pageIndex, annotation, newField, documentId),
|
|
1119
|
-
renameField: (annotationId, name) => this.renameFieldMethod(annotationId, name, documentId),
|
|
1120
|
-
shareField: (annotationId, targetAnnotationId) => this.shareFieldMethod(annotationId, targetAnnotationId, documentId),
|
|
1121
|
-
renderWidget: (options) => this.renderWidget(options, documentId),
|
|
1122
|
-
selectField: (annotationId) => this.selectFieldMethod(annotationId, documentId),
|
|
1123
|
-
deselectField: () => this.deselectFieldMethod(documentId),
|
|
1124
|
-
getSelectedFieldId: () => this.getSelectedFieldId(documentId),
|
|
1125
|
-
selectNextField: () => this.selectNextFieldMethod(documentId),
|
|
1126
|
-
selectPreviousField: () => this.selectPreviousFieldMethod(documentId),
|
|
1127
|
-
activateField: () => this.activateFieldMethod(documentId),
|
|
1128
|
-
getState: () => this.getDocumentState(documentId),
|
|
1129
|
-
getFieldGroup: (annotationId) => this.getFieldGroup(annotationId, documentId),
|
|
1130
|
-
getFieldSiblings: (annotationId) => this.getFieldSiblings(annotationId, documentId),
|
|
1131
|
-
getFormValues: () => this.getFormValuesMethod(documentId),
|
|
1132
|
-
getFormFields: () => this.getFormFieldsMethod(documentId),
|
|
1133
|
-
setFormValues: (values) => this.setFormValuesMethod(values, documentId),
|
|
1134
|
-
onStateChange: this.state$.forScope(documentId),
|
|
1135
|
-
onFieldValueChange: this.fieldValueChange$.forScope(documentId),
|
|
1136
|
-
onFormReady: this.formReady$.forScope(documentId)
|
|
1137
|
-
};
|
|
1138
|
-
}
|
|
1139
|
-
handleAnnotationEvent(event) {
|
|
1140
|
-
switch (event.type) {
|
|
1141
|
-
case "loaded":
|
|
1142
|
-
this.buildFieldGroupIndex(event.documentId);
|
|
1143
|
-
this.formReady$.emit(event.documentId, this.getFormFieldsMethod(event.documentId));
|
|
1144
|
-
break;
|
|
1145
|
-
case "create":
|
|
1146
|
-
case "delete": {
|
|
1147
|
-
if (event.annotation.type !== PdfAnnotationSubtype.WIDGET) break;
|
|
1148
|
-
this.buildFieldGroupIndex(event.documentId);
|
|
1149
|
-
break;
|
|
1150
|
-
}
|
|
1151
|
-
case "update": {
|
|
1152
|
-
if (!event.committed) break;
|
|
1153
|
-
const anno = event.annotation;
|
|
1154
|
-
if (anno.type !== PdfAnnotationSubtype.WIDGET) break;
|
|
1155
|
-
if (this.propagationInProgress.has(anno.id)) {
|
|
1156
|
-
this.propagationInProgress.delete(anno.id);
|
|
1157
|
-
break;
|
|
1158
|
-
}
|
|
1159
|
-
const patch = event.patch;
|
|
1160
|
-
this.buildFieldGroupIndex(event.documentId);
|
|
1161
|
-
this.propagateFieldLevelChanges(event.documentId, anno, patch);
|
|
1162
|
-
break;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
}
|
|
1166
|
-
propagateFieldLevelChanges(documentId, annotation, patch) {
|
|
1167
|
-
if (!this.annotation || !patch.field) return;
|
|
1168
|
-
const fieldPatch = patch.field;
|
|
1169
|
-
const siblings = this.getFieldSiblings(annotation.id, documentId);
|
|
1170
|
-
if (siblings.length === 0) return;
|
|
1171
|
-
const siblingPatches = [];
|
|
1172
|
-
for (const sibling of siblings) {
|
|
1173
|
-
this.propagationInProgress.add(sibling.annotationId);
|
|
1174
|
-
siblingPatches.push({
|
|
1175
|
-
pageIndex: sibling.pageIndex,
|
|
1176
|
-
id: sibling.annotationId,
|
|
1177
|
-
patch: { field: { ...fieldPatch } }
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
this.annotation.forDocument(documentId).updateAnnotations(siblingPatches);
|
|
1181
|
-
}
|
|
1182
|
-
buildFieldGroupIndex(documentId) {
|
|
1183
|
-
if (!this.annotation) return;
|
|
1184
|
-
const annoState = this.annotation.forDocument(documentId).getState();
|
|
1185
|
-
if (!annoState) return;
|
|
1186
|
-
const idx = /* @__PURE__ */ new Map();
|
|
1187
|
-
const allWidgets = [];
|
|
1188
|
-
for (const [pageStr, uids] of Object.entries(annoState.pages)) {
|
|
1189
|
-
const pageIndex = Number(pageStr);
|
|
1190
|
-
for (const uid of uids) {
|
|
1191
|
-
const tracked = annoState.byUid[uid];
|
|
1192
|
-
if (!tracked || tracked.object.type !== PdfAnnotationSubtype.WIDGET) continue;
|
|
1193
|
-
const widget = tracked.object;
|
|
1194
|
-
const entry = { annotationId: uid, pageIndex };
|
|
1195
|
-
allWidgets.push({ entry, widget });
|
|
1196
|
-
const fieldKey = this.getFieldKey(widget.field);
|
|
1197
|
-
if (!fieldKey) continue;
|
|
1198
|
-
const group = idx.get(fieldKey) ?? [];
|
|
1199
|
-
group.push(entry);
|
|
1200
|
-
idx.set(fieldKey, group);
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
this.fieldGroupIndex.set(documentId, idx);
|
|
1204
|
-
const nameIdx = /* @__PURE__ */ new Map();
|
|
1205
|
-
for (const { entry, widget } of allWidgets) {
|
|
1206
|
-
const name = widget.field.name.trim();
|
|
1207
|
-
if (!name) continue;
|
|
1208
|
-
const group = nameIdx.get(name) ?? [];
|
|
1209
|
-
group.push(entry);
|
|
1210
|
-
nameIdx.set(name, group);
|
|
1211
|
-
}
|
|
1212
|
-
this.fieldNameIndex.set(documentId, nameIdx);
|
|
1213
|
-
const navigable = allWidgets.filter(
|
|
1214
|
-
({ widget }) => !(widget.field.flag & PDF_FORM_FIELD_FLAG.READONLY)
|
|
1215
|
-
);
|
|
1216
|
-
this.orderedFieldIndex.set(
|
|
1217
|
-
documentId,
|
|
1218
|
-
navigable.map(({ entry }) => entry)
|
|
1219
|
-
);
|
|
1220
|
-
}
|
|
1221
|
-
getDocumentState(documentId) {
|
|
1222
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
1223
|
-
return this.state.documents[id] ?? { ...initialDocumentState };
|
|
1224
|
-
}
|
|
1225
|
-
// ─────────────────────────────────────────────────────────
|
|
1226
|
-
// Field Group Index
|
|
1227
|
-
// ─────────────────────────────────────────────────────────
|
|
1228
|
-
getDocIndex(documentId) {
|
|
1229
|
-
let idx = this.fieldGroupIndex.get(documentId);
|
|
1230
|
-
if (!idx) {
|
|
1231
|
-
idx = /* @__PURE__ */ new Map();
|
|
1232
|
-
this.fieldGroupIndex.set(documentId, idx);
|
|
1233
|
-
}
|
|
1234
|
-
return idx;
|
|
1235
|
-
}
|
|
1236
|
-
getFieldKey(field) {
|
|
1237
|
-
if (typeof field.fieldObjectId === "number" && field.fieldObjectId > 0) {
|
|
1238
|
-
return `field:${field.fieldObjectId}`;
|
|
1239
|
-
}
|
|
1240
|
-
const normalizedName = field.name.trim();
|
|
1241
|
-
if (!normalizedName) return null;
|
|
1242
|
-
return `legacy:${field.type}:${normalizedName}`;
|
|
1243
|
-
}
|
|
1244
|
-
resolveWidgetAnnotation(annotationId, documentId) {
|
|
1245
|
-
if (!this.annotation) return null;
|
|
1246
|
-
const annoState = this.annotation.forDocument(documentId).getState();
|
|
1247
|
-
const tracked = annoState == null ? void 0 : annoState.byUid[annotationId];
|
|
1248
|
-
if (!tracked || tracked.object.type !== PdfAnnotationSubtype.WIDGET) {
|
|
1249
|
-
return null;
|
|
1250
|
-
}
|
|
1251
|
-
return tracked.object;
|
|
1252
|
-
}
|
|
1253
|
-
getFieldGroup(annotationId, documentId) {
|
|
1254
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1255
|
-
const widget = this.resolveWidgetAnnotation(annotationId, docId);
|
|
1256
|
-
if (!widget) return [];
|
|
1257
|
-
const fieldKey = this.getFieldKey(widget.field);
|
|
1258
|
-
if (!fieldKey) {
|
|
1259
|
-
return [{ annotationId: widget.id, pageIndex: widget.pageIndex }];
|
|
1260
|
-
}
|
|
1261
|
-
return this.getDocIndex(docId).get(fieldKey) ?? [
|
|
1262
|
-
{ annotationId: widget.id, pageIndex: widget.pageIndex }
|
|
1263
|
-
];
|
|
1264
|
-
}
|
|
1265
|
-
getFieldSiblings(annotationId, documentId) {
|
|
1266
|
-
return this.getFieldGroup(annotationId, documentId).filter(
|
|
1267
|
-
(e) => e.annotationId !== annotationId
|
|
1268
|
-
);
|
|
1269
|
-
}
|
|
1270
|
-
// ─────────────────────────────────────────────────────────
|
|
1271
|
-
// Form Values API
|
|
1272
|
-
// ─────────────────────────────────────────────────────────
|
|
1273
|
-
getFormValuesMethod(documentId) {
|
|
1274
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1275
|
-
const nameIdx = this.fieldNameIndex.get(docId);
|
|
1276
|
-
if (!nameIdx) return {};
|
|
1277
|
-
const values = {};
|
|
1278
|
-
for (const [name, entries] of nameIdx) {
|
|
1279
|
-
const widget = this.resolveWidgetAnnotation(entries[0].annotationId, docId);
|
|
1280
|
-
if (widget) {
|
|
1281
|
-
values[name] = widget.field.value;
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
return values;
|
|
1285
|
-
}
|
|
1286
|
-
getFormFieldsMethod(documentId) {
|
|
1287
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1288
|
-
const nameIdx = this.fieldNameIndex.get(docId);
|
|
1289
|
-
if (!nameIdx) return [];
|
|
1290
|
-
const fields = [];
|
|
1291
|
-
for (const [name, entries] of nameIdx) {
|
|
1292
|
-
const widget = this.resolveWidgetAnnotation(entries[0].annotationId, docId);
|
|
1293
|
-
if (!widget) continue;
|
|
1294
|
-
fields.push({
|
|
1295
|
-
name,
|
|
1296
|
-
type: widget.field.type,
|
|
1297
|
-
value: widget.field.value,
|
|
1298
|
-
readOnly: !!(widget.field.flag & PDF_FORM_FIELD_FLAG.READONLY),
|
|
1299
|
-
..."options" in widget.field && widget.field.options ? { options: widget.field.options } : {}
|
|
1300
|
-
});
|
|
1301
|
-
}
|
|
1302
|
-
return fields;
|
|
1303
|
-
}
|
|
1304
|
-
setFormValuesMethod(values, documentId) {
|
|
1305
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1306
|
-
const nameIdx = this.fieldNameIndex.get(docId);
|
|
1307
|
-
if (!nameIdx) {
|
|
1308
|
-
return PdfTaskHelper.reject({
|
|
1309
|
-
code: PdfErrorCode.NotFound,
|
|
1310
|
-
message: "no form fields indexed for this document"
|
|
1311
|
-
});
|
|
1312
|
-
}
|
|
1313
|
-
const entries = Object.entries(values);
|
|
1314
|
-
if (entries.length === 0) {
|
|
1315
|
-
return PdfTaskHelper.resolve(true);
|
|
1316
|
-
}
|
|
1317
|
-
const resultTask = new Task();
|
|
1318
|
-
let pending = entries.length;
|
|
1319
|
-
let failed = false;
|
|
1320
|
-
const onComplete = () => {
|
|
1321
|
-
pending--;
|
|
1322
|
-
if (pending === 0 && !failed) {
|
|
1323
|
-
resultTask.resolve(true);
|
|
1324
|
-
}
|
|
1325
|
-
};
|
|
1326
|
-
const onError = (err) => {
|
|
1327
|
-
if (!failed) {
|
|
1328
|
-
failed = true;
|
|
1329
|
-
resultTask.reject(err);
|
|
1330
|
-
}
|
|
1331
|
-
};
|
|
1332
|
-
for (const [name, value] of entries) {
|
|
1333
|
-
const fieldEntries = nameIdx.get(name);
|
|
1334
|
-
if (!fieldEntries || fieldEntries.length === 0) {
|
|
1335
|
-
onComplete();
|
|
1336
|
-
continue;
|
|
1337
|
-
}
|
|
1338
|
-
let targetEntry = fieldEntries[0];
|
|
1339
|
-
let widget = this.resolveWidgetAnnotation(targetEntry.annotationId, docId);
|
|
1340
|
-
if (!widget) {
|
|
1341
|
-
onComplete();
|
|
1342
|
-
continue;
|
|
1343
|
-
}
|
|
1344
|
-
if (widget.field.type === PDF_FORM_FIELD_TYPE.RADIOBUTTON || widget.field.type === PDF_FORM_FIELD_TYPE.CHECKBOX) {
|
|
1345
|
-
for (const entry of fieldEntries) {
|
|
1346
|
-
const w = this.resolveWidgetAnnotation(entry.annotationId, docId);
|
|
1347
|
-
if (w && w.exportValue === value) {
|
|
1348
|
-
widget = w;
|
|
1349
|
-
targetEntry = entry;
|
|
1350
|
-
break;
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
}
|
|
1354
|
-
this.setFormFieldValues(
|
|
1355
|
-
targetEntry.pageIndex,
|
|
1356
|
-
widget,
|
|
1357
|
-
this.buildImportField(widget, value),
|
|
1358
|
-
docId
|
|
1359
|
-
).wait(
|
|
1360
|
-
() => onComplete(),
|
|
1361
|
-
(error) => onError(error.reason)
|
|
1362
|
-
);
|
|
1363
|
-
}
|
|
1364
|
-
return resultTask;
|
|
1365
|
-
}
|
|
1366
|
-
buildImportField(widget, value) {
|
|
1367
|
-
const field = widget.field;
|
|
1368
|
-
if ((field.type === PDF_FORM_FIELD_TYPE.COMBOBOX || field.type === PDF_FORM_FIELD_TYPE.LISTBOX) && "options" in field) {
|
|
1369
|
-
return {
|
|
1370
|
-
...field,
|
|
1371
|
-
value,
|
|
1372
|
-
options: field.options.map((opt) => ({
|
|
1373
|
-
...opt,
|
|
1374
|
-
isSelected: opt.label === value
|
|
1375
|
-
}))
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
if (field.type === PDF_FORM_FIELD_TYPE.CHECKBOX) {
|
|
1379
|
-
const normalizedValue = value === "Off" ? "Off" : widget.exportValue ?? "Yes";
|
|
1380
|
-
return { ...field, value: normalizedValue };
|
|
1381
|
-
}
|
|
1382
|
-
return { ...field, value };
|
|
1383
|
-
}
|
|
1384
|
-
isToggleField(field) {
|
|
1385
|
-
return field.type === PDF_FORM_FIELD_TYPE.CHECKBOX || field.type === PDF_FORM_FIELD_TYPE.RADIOBUTTON;
|
|
1386
|
-
}
|
|
1387
|
-
shouldRegenerateWidgetAppearances(field) {
|
|
1388
|
-
return !this.isToggleField(field);
|
|
1389
|
-
}
|
|
1390
|
-
getFieldBatchTarget(batch) {
|
|
1391
|
-
const entries = [...batch.values()];
|
|
1392
|
-
const checkedToggleEntry = entries.find(
|
|
1393
|
-
(entry) => this.isToggleField(entry.widget.field) && isWidgetChecked(entry.widget)
|
|
1394
|
-
);
|
|
1395
|
-
return checkedToggleEntry ?? entries[0];
|
|
1396
|
-
}
|
|
1397
|
-
async readFieldWidgets(doc, groupEntries, seq, regenerateAppearances) {
|
|
1398
|
-
const pageSet = new Set(groupEntries.map((entry) => entry.pageIndex));
|
|
1399
|
-
const memberIds = new Set(groupEntries.map((entry) => entry.annotationId));
|
|
1400
|
-
const batch = /* @__PURE__ */ new Map();
|
|
1401
|
-
for (const pageIndex of pageSet) {
|
|
1402
|
-
const page = doc.pages.find((entry) => entry.index === pageIndex);
|
|
1403
|
-
if (!page) continue;
|
|
1404
|
-
const pageMemberIds = groupEntries.filter((entry) => entry.pageIndex === pageIndex).map((entry) => entry.annotationId);
|
|
1405
|
-
if (regenerateAppearances) {
|
|
1406
|
-
await seq.run(() => this.engine.regenerateWidgetAppearances(doc, page, pageMemberIds));
|
|
1407
|
-
}
|
|
1408
|
-
const widgets = await seq.run(() => this.engine.getPageAnnoWidgets(doc, page));
|
|
1409
|
-
for (const widget of widgets) {
|
|
1410
|
-
if (memberIds.has(widget.id)) {
|
|
1411
|
-
batch.set(widget.id, { widget, pageIndex });
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
return batch;
|
|
1416
|
-
}
|
|
1417
|
-
syncFieldBatch(documentId, batch, options = {}) {
|
|
1418
|
-
if (!this.annotation) return;
|
|
1419
|
-
for (const [id, { widget, pageIndex }] of batch) {
|
|
1420
|
-
this.annotation.syncAnnotationObject(id, widget, documentId);
|
|
1421
|
-
this.annotation.invalidatePageAppearances(pageIndex, documentId);
|
|
1422
|
-
if (options.emitFieldValueChanges) {
|
|
1423
|
-
this.fieldValueChange$.emit(documentId, {
|
|
1424
|
-
documentId,
|
|
1425
|
-
pageIndex,
|
|
1426
|
-
annotationId: id,
|
|
1427
|
-
widget
|
|
1428
|
-
});
|
|
1429
|
-
}
|
|
1430
|
-
}
|
|
1431
|
-
this.buildFieldGroupIndex(documentId);
|
|
1432
|
-
}
|
|
1433
|
-
resolveWidgetPage(annotationId, documentId, doc) {
|
|
1434
|
-
const widget = this.resolveWidgetAnnotation(annotationId, documentId);
|
|
1435
|
-
if (!widget) return null;
|
|
1436
|
-
const page = doc.pages.find((entry) => entry.index === widget.pageIndex);
|
|
1437
|
-
if (!page) return null;
|
|
1438
|
-
return { widget, page };
|
|
1439
|
-
}
|
|
1440
|
-
getPageFormAnnoWidgets(pageIndex, documentId) {
|
|
1441
|
-
const docState = this.getCoreDocumentOrThrow(documentId);
|
|
1442
|
-
const doc = docState.document;
|
|
1443
|
-
if (!doc) {
|
|
1444
|
-
return PdfTaskHelper.reject({
|
|
1445
|
-
code: PdfErrorCode.DocNotOpen,
|
|
1446
|
-
message: "document is not open"
|
|
1447
|
-
});
|
|
1448
|
-
}
|
|
1449
|
-
const page = doc.pages.find((p) => p.index === pageIndex);
|
|
1450
|
-
if (!page) {
|
|
1451
|
-
return PdfTaskHelper.reject({
|
|
1452
|
-
code: PdfErrorCode.NotFound,
|
|
1453
|
-
message: "page does not exist"
|
|
1454
|
-
});
|
|
1455
|
-
}
|
|
1456
|
-
return this.engine.getPageAnnoWidgets(doc, page);
|
|
1457
|
-
}
|
|
1458
|
-
setFormFieldValues(pageIndex, annotation, newField, documentId) {
|
|
1459
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1460
|
-
const coreDoc = this.getCoreDocumentOrThrow(docId);
|
|
1461
|
-
const doc = coreDoc.document;
|
|
1462
|
-
if (!doc) {
|
|
1463
|
-
return PdfTaskHelper.reject({
|
|
1464
|
-
code: PdfErrorCode.DocNotOpen,
|
|
1465
|
-
message: "document is not open"
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
const page = doc.pages.find((p) => p.index === pageIndex);
|
|
1469
|
-
if (!page) {
|
|
1470
|
-
return PdfTaskHelper.reject({
|
|
1471
|
-
code: PdfErrorCode.NotFound,
|
|
1472
|
-
message: "page does not exist"
|
|
1473
|
-
});
|
|
1474
|
-
}
|
|
1475
|
-
const resultTask = new Task();
|
|
1476
|
-
const seq = new TaskSequence(resultTask);
|
|
1477
|
-
seq.execute(
|
|
1478
|
-
async () => {
|
|
1479
|
-
const groupEntries = this.getFieldGroup(annotation.id, docId);
|
|
1480
|
-
const beforeEntries = await this.readFieldWidgets(doc, groupEntries, seq, false);
|
|
1481
|
-
await seq.run(() => this.engine.setFormFieldState(doc, page, annotation, newField));
|
|
1482
|
-
const afterEntries = await this.readFieldWidgets(
|
|
1483
|
-
doc,
|
|
1484
|
-
groupEntries,
|
|
1485
|
-
seq,
|
|
1486
|
-
this.shouldRegenerateWidgetAppearances(newField)
|
|
1487
|
-
);
|
|
1488
|
-
let isFirstExecution = true;
|
|
1489
|
-
const applyToEngine = async (batch) => {
|
|
1490
|
-
const target = this.getFieldBatchTarget(batch);
|
|
1491
|
-
if (!target) return;
|
|
1492
|
-
const pg = doc.pages.find((p) => p.index === target.pageIndex);
|
|
1493
|
-
if (!pg) return;
|
|
1494
|
-
await new Promise((resolve) => {
|
|
1495
|
-
this.engine.setFormFieldState(doc, pg, target.widget, target.widget.field).wait(
|
|
1496
|
-
() => resolve(),
|
|
1497
|
-
() => resolve()
|
|
1498
|
-
);
|
|
1499
|
-
});
|
|
1500
|
-
if (this.shouldRegenerateWidgetAppearances(target.widget.field)) {
|
|
1501
|
-
for (const pi of new Set([...batch.values()].map((entry) => entry.pageIndex))) {
|
|
1502
|
-
const p = doc.pages.find((pp) => pp.index === pi);
|
|
1503
|
-
if (!p) continue;
|
|
1504
|
-
const ids = [...batch.entries()].filter(([, entry]) => entry.pageIndex === pi).map(([id]) => id);
|
|
1505
|
-
await new Promise((resolve) => {
|
|
1506
|
-
this.engine.regenerateWidgetAppearances(doc, p, ids).wait(
|
|
1507
|
-
() => resolve(),
|
|
1508
|
-
() => resolve()
|
|
1509
|
-
);
|
|
1510
|
-
});
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
};
|
|
1514
|
-
const command = {
|
|
1515
|
-
execute: () => {
|
|
1516
|
-
const skipEngine = isFirstExecution;
|
|
1517
|
-
isFirstExecution = false;
|
|
1518
|
-
if (skipEngine) {
|
|
1519
|
-
this.syncFieldBatch(docId, afterEntries, { emitFieldValueChanges: true });
|
|
1520
|
-
} else {
|
|
1521
|
-
applyToEngine(afterEntries).then(
|
|
1522
|
-
() => this.syncFieldBatch(docId, afterEntries, { emitFieldValueChanges: true })
|
|
1523
|
-
);
|
|
1524
|
-
}
|
|
1525
|
-
},
|
|
1526
|
-
undo: () => {
|
|
1527
|
-
applyToEngine(beforeEntries).then(
|
|
1528
|
-
() => this.syncFieldBatch(docId, beforeEntries, { emitFieldValueChanges: true })
|
|
1529
|
-
);
|
|
1530
|
-
}
|
|
1531
|
-
};
|
|
1532
|
-
if (this.history) {
|
|
1533
|
-
this.history.forDocument(docId).register(command, this.FORM_HISTORY_TOPIC);
|
|
1534
|
-
} else {
|
|
1535
|
-
command.execute();
|
|
1536
|
-
}
|
|
1537
|
-
resultTask.resolve(true);
|
|
1538
|
-
},
|
|
1539
|
-
(err) => ({ code: PdfErrorCode.Unknown, message: String(err) })
|
|
1540
|
-
);
|
|
1541
|
-
return resultTask;
|
|
1542
|
-
}
|
|
1543
|
-
findConflictingFieldGroups(normalizedName, sourceAnnotationId, documentId) {
|
|
1544
|
-
const result = /* @__PURE__ */ new Map();
|
|
1545
|
-
if (!this.annotation) return result;
|
|
1546
|
-
const annoState = this.annotation.forDocument(documentId).getState();
|
|
1547
|
-
if (!annoState) return result;
|
|
1548
|
-
const sourceFieldKey = (() => {
|
|
1549
|
-
const w = this.resolveWidgetAnnotation(sourceAnnotationId, documentId);
|
|
1550
|
-
return w ? this.getFieldKey(w.field) : null;
|
|
1551
|
-
})();
|
|
1552
|
-
for (const [pageStr, uids] of Object.entries(annoState.pages)) {
|
|
1553
|
-
const pageIndex = Number(pageStr);
|
|
1554
|
-
for (const uid of uids) {
|
|
1555
|
-
const tracked = annoState.byUid[uid];
|
|
1556
|
-
if (!tracked || tracked.object.type !== PdfAnnotationSubtype.WIDGET) continue;
|
|
1557
|
-
const widget = tracked.object;
|
|
1558
|
-
if (widget.field.name.trim() !== normalizedName) continue;
|
|
1559
|
-
const fieldKey = this.getFieldKey(widget.field);
|
|
1560
|
-
if (!fieldKey || fieldKey === sourceFieldKey) continue;
|
|
1561
|
-
if (!result.has(fieldKey)) {
|
|
1562
|
-
result.set(fieldKey, { entries: [], widget });
|
|
1563
|
-
}
|
|
1564
|
-
result.get(fieldKey).entries.push({ annotationId: uid, pageIndex });
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1567
|
-
return result;
|
|
1568
|
-
}
|
|
1569
|
-
renameFieldMethod(annotationId, name, documentId) {
|
|
1570
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1571
|
-
const normalizedName = name.trim();
|
|
1572
|
-
if (!normalizedName) {
|
|
1573
|
-
return PdfTaskHelper.reject({
|
|
1574
|
-
code: PdfErrorCode.Unknown,
|
|
1575
|
-
message: "Field name must not be empty."
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
const coreDoc = this.getCoreDocumentOrThrow(docId);
|
|
1579
|
-
const doc = coreDoc.document;
|
|
1580
|
-
if (!doc) {
|
|
1581
|
-
return PdfTaskHelper.reject({
|
|
1582
|
-
code: PdfErrorCode.DocNotOpen,
|
|
1583
|
-
message: "document is not open"
|
|
1584
|
-
});
|
|
1585
|
-
}
|
|
1586
|
-
const source = this.resolveWidgetPage(annotationId, docId, doc);
|
|
1587
|
-
if (!source) {
|
|
1588
|
-
return PdfTaskHelper.reject({
|
|
1589
|
-
code: PdfErrorCode.NotFound,
|
|
1590
|
-
message: "widget annotation not found"
|
|
1591
|
-
});
|
|
1592
|
-
}
|
|
1593
|
-
if (source.widget.field.name.trim() === normalizedName) {
|
|
1594
|
-
return PdfTaskHelper.resolve({ outcome: "no-op" });
|
|
1595
|
-
}
|
|
1596
|
-
const conflicts = this.findConflictingFieldGroups(normalizedName, annotationId, docId);
|
|
1597
|
-
if (conflicts.size > 0) {
|
|
1598
|
-
if (conflicts.size > 1) {
|
|
1599
|
-
return PdfTaskHelper.reject({
|
|
1600
|
-
code: PdfErrorCode.Unknown,
|
|
1601
|
-
message: "That field name is used by multiple fields. Choose a unique name."
|
|
1602
|
-
});
|
|
1603
|
-
}
|
|
1604
|
-
const [, match] = [...conflicts.entries()][0];
|
|
1605
|
-
if (match.widget.field.type !== source.widget.field.type) {
|
|
1606
|
-
return PdfTaskHelper.reject({
|
|
1607
|
-
code: PdfErrorCode.Unknown,
|
|
1608
|
-
message: "That field name is already used by a different widget type."
|
|
1609
|
-
});
|
|
1610
|
-
}
|
|
1611
|
-
return PdfTaskHelper.resolve({
|
|
1612
|
-
outcome: "conflict",
|
|
1613
|
-
targetAnnotationId: match.entries[0].annotationId,
|
|
1614
|
-
fieldName: normalizedName
|
|
1615
|
-
});
|
|
1616
|
-
}
|
|
1617
|
-
const resultTask = new Task();
|
|
1618
|
-
const seq = new TaskSequence(resultTask);
|
|
1619
|
-
seq.execute(
|
|
1620
|
-
async () => {
|
|
1621
|
-
const groupEntries = this.getFieldGroup(annotationId, docId);
|
|
1622
|
-
await seq.run(
|
|
1623
|
-
() => this.engine.renameWidgetField(doc, source.page, source.widget, normalizedName)
|
|
1624
|
-
);
|
|
1625
|
-
const syncedEntries = await this.readFieldWidgets(doc, groupEntries, seq, false);
|
|
1626
|
-
this.syncFieldBatch(docId, syncedEntries);
|
|
1627
|
-
resultTask.resolve({ outcome: "renamed" });
|
|
1628
|
-
},
|
|
1629
|
-
(err) => ({ code: PdfErrorCode.Unknown, message: String(err) })
|
|
1630
|
-
);
|
|
1631
|
-
return resultTask;
|
|
1632
|
-
}
|
|
1633
|
-
shareFieldMethod(annotationId, targetAnnotationId, documentId) {
|
|
1634
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1635
|
-
if (annotationId === targetAnnotationId) {
|
|
1636
|
-
return PdfTaskHelper.resolve(true);
|
|
1637
|
-
}
|
|
1638
|
-
const coreDoc = this.getCoreDocumentOrThrow(docId);
|
|
1639
|
-
const doc = coreDoc.document;
|
|
1640
|
-
if (!doc) {
|
|
1641
|
-
return PdfTaskHelper.reject({
|
|
1642
|
-
code: PdfErrorCode.DocNotOpen,
|
|
1643
|
-
message: "document is not open"
|
|
1644
|
-
});
|
|
1645
|
-
}
|
|
1646
|
-
const source = this.resolveWidgetPage(annotationId, docId, doc);
|
|
1647
|
-
const target = this.resolveWidgetPage(targetAnnotationId, docId, doc);
|
|
1648
|
-
if (!source || !target) {
|
|
1649
|
-
return PdfTaskHelper.reject({
|
|
1650
|
-
code: PdfErrorCode.NotFound,
|
|
1651
|
-
message: "widget annotation not found"
|
|
1652
|
-
});
|
|
1653
|
-
}
|
|
1654
|
-
if (source.widget.field.type !== target.widget.field.type) {
|
|
1655
|
-
return PdfTaskHelper.reject({
|
|
1656
|
-
code: PdfErrorCode.Unknown,
|
|
1657
|
-
message: "cannot share fields with different widget types"
|
|
1658
|
-
});
|
|
1659
|
-
}
|
|
1660
|
-
if (source.widget.field.fieldObjectId && target.widget.field.fieldObjectId && source.widget.field.fieldObjectId === target.widget.field.fieldObjectId) {
|
|
1661
|
-
return PdfTaskHelper.resolve(true);
|
|
1662
|
-
}
|
|
1663
|
-
const affectedEntryMap = /* @__PURE__ */ new Map();
|
|
1664
|
-
for (const entry of this.getFieldGroup(annotationId, docId)) {
|
|
1665
|
-
affectedEntryMap.set(entry.annotationId, entry);
|
|
1666
|
-
}
|
|
1667
|
-
for (const entry of this.getFieldGroup(targetAnnotationId, docId)) {
|
|
1668
|
-
affectedEntryMap.set(entry.annotationId, entry);
|
|
1669
|
-
}
|
|
1670
|
-
const affectedEntries = [...affectedEntryMap.values()];
|
|
1671
|
-
const resultTask = new Task();
|
|
1672
|
-
const seq = new TaskSequence(resultTask);
|
|
1673
|
-
seq.execute(
|
|
1674
|
-
async () => {
|
|
1675
|
-
await seq.run(
|
|
1676
|
-
() => this.engine.shareWidgetField(doc, source.page, source.widget, target.page, target.widget)
|
|
1677
|
-
);
|
|
1678
|
-
const syncedEntries = await this.readFieldWidgets(
|
|
1679
|
-
doc,
|
|
1680
|
-
affectedEntries,
|
|
1681
|
-
seq,
|
|
1682
|
-
this.shouldRegenerateWidgetAppearances(target.widget.field)
|
|
1683
|
-
);
|
|
1684
|
-
this.syncFieldBatch(docId, syncedEntries);
|
|
1685
|
-
resultTask.resolve(true);
|
|
1686
|
-
},
|
|
1687
|
-
(err) => ({ code: PdfErrorCode.Unknown, message: String(err) })
|
|
1688
|
-
);
|
|
1689
|
-
return resultTask;
|
|
1690
|
-
}
|
|
1691
|
-
selectFieldMethod(annotationId, documentId) {
|
|
1692
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1693
|
-
this.dispatch(selectField(docId, annotationId));
|
|
1694
|
-
}
|
|
1695
|
-
deselectFieldMethod(documentId) {
|
|
1696
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1697
|
-
this.dispatch(deselectField(docId));
|
|
1698
|
-
}
|
|
1699
|
-
getSelectedFieldId(documentId) {
|
|
1700
|
-
return this.getDocumentState(documentId).selectedFieldId;
|
|
1701
|
-
}
|
|
1702
|
-
selectNextFieldMethod(documentId) {
|
|
1703
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1704
|
-
const ordered = this.orderedFieldIndex.get(docId);
|
|
1705
|
-
if (!ordered || ordered.length === 0) return;
|
|
1706
|
-
const currentId = this.getSelectedFieldId(docId);
|
|
1707
|
-
let nextIndex = 0;
|
|
1708
|
-
if (currentId) {
|
|
1709
|
-
const currentIndex = ordered.findIndex((e) => e.annotationId === currentId);
|
|
1710
|
-
nextIndex = currentIndex >= 0 ? (currentIndex + 1) % ordered.length : 0;
|
|
1711
|
-
}
|
|
1712
|
-
const next = ordered[nextIndex];
|
|
1713
|
-
this.dispatch(selectField(docId, next.annotationId));
|
|
1714
|
-
}
|
|
1715
|
-
selectPreviousFieldMethod(documentId) {
|
|
1716
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1717
|
-
const ordered = this.orderedFieldIndex.get(docId);
|
|
1718
|
-
if (!ordered || ordered.length === 0) return;
|
|
1719
|
-
const currentId = this.getSelectedFieldId(docId);
|
|
1720
|
-
let prevIndex = ordered.length - 1;
|
|
1721
|
-
if (currentId) {
|
|
1722
|
-
const currentIndex = ordered.findIndex((e) => e.annotationId === currentId);
|
|
1723
|
-
prevIndex = currentIndex >= 0 ? (currentIndex - 1 + ordered.length) % ordered.length : ordered.length - 1;
|
|
1724
|
-
}
|
|
1725
|
-
const prev = ordered[prevIndex];
|
|
1726
|
-
this.dispatch(selectField(docId, prev.annotationId));
|
|
1727
|
-
}
|
|
1728
|
-
activateFieldMethod(documentId) {
|
|
1729
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1730
|
-
const selectedId = this.getSelectedFieldId(docId);
|
|
1731
|
-
if (!selectedId) return;
|
|
1732
|
-
const widget = this.resolveWidgetAnnotation(selectedId, docId);
|
|
1733
|
-
if (!widget) return;
|
|
1734
|
-
const field = widget.field;
|
|
1735
|
-
if (field.type === PDF_FORM_FIELD_TYPE.CHECKBOX) {
|
|
1736
|
-
const checked = isWidgetChecked(widget);
|
|
1737
|
-
const newValue = checked ? "Off" : widget.exportValue ?? "Yes";
|
|
1738
|
-
this.setFormFieldValues(widget.pageIndex, widget, { ...field, value: newValue }, docId);
|
|
1739
|
-
} else if (field.type === PDF_FORM_FIELD_TYPE.RADIOBUTTON) {
|
|
1740
|
-
if (!isWidgetChecked(widget) && widget.exportValue) {
|
|
1741
|
-
this.setFormFieldValues(
|
|
1742
|
-
widget.pageIndex,
|
|
1743
|
-
widget,
|
|
1744
|
-
{ ...field, value: widget.exportValue },
|
|
1745
|
-
docId
|
|
1746
|
-
);
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
renderWidget(options, documentId) {
|
|
1751
|
-
if (!this.annotation) {
|
|
1752
|
-
return PdfTaskHelper.reject({
|
|
1753
|
-
code: PdfErrorCode.NotFound,
|
|
1754
|
-
message: "annotation plugin not found"
|
|
1755
|
-
});
|
|
1756
|
-
}
|
|
1757
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
1758
|
-
return this.annotation.forDocument(id).renderAnnotation(options);
|
|
1759
|
-
}
|
|
1760
|
-
async destroy() {
|
|
1761
|
-
this.state$.clear();
|
|
1762
|
-
this.fieldValueChange$.clear();
|
|
1763
|
-
this.formReady$.clear();
|
|
1764
|
-
super.destroy();
|
|
1765
|
-
}
|
|
1766
|
-
};
|
|
1767
|
-
_FormPlugin.id = "form";
|
|
1768
|
-
let FormPlugin = _FormPlugin;
|
|
1769
|
-
const FormPluginPackage = {
|
|
1770
|
-
manifest,
|
|
1771
|
-
create: (registry, config) => new FormPlugin(FORM_PLUGIN_ID, registry, config),
|
|
1772
|
-
reducer,
|
|
1773
|
-
initialState
|
|
1774
|
-
};
|
|
1775
|
-
export {
|
|
1776
|
-
FORM_PLUGIN_ID,
|
|
1777
|
-
FormPlugin,
|
|
1778
|
-
FormPluginPackage,
|
|
1779
|
-
formCheckboxTool,
|
|
1780
|
-
formComboboxTool,
|
|
1781
|
-
formListboxTool,
|
|
1782
|
-
formRadioButtonTool,
|
|
1783
|
-
formTextFieldTool,
|
|
1784
|
-
formTools,
|
|
1785
|
-
initialDocumentState,
|
|
1786
|
-
initialState,
|
|
1787
|
-
manifest
|
|
1788
|
-
};
|
|
1789
|
-
//# sourceMappingURL=index.js.map
|
|
1405
|
+
//#endregion
|
|
1406
|
+
export { FORM_TOOLS, FORM_TOOL_BY_ID, FormScriptingController, FormToken, createFormScriptingController, createSerialMutationQueue, fieldKeyOf, formPlugin };
|
|
1407
|
+
|
|
1408
|
+
//# sourceMappingURL=index.js.map
|