@embedpdf/plugin-redaction 2.15.0 → 3.0.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +192 -21
- package/README.md +109 -0
- package/dist/index.cjs +227 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -0
- package/dist/index.d.ts +89 -1
- package/dist/index.js +220 -1532
- package/dist/index.js.map +1 -1
- package/package.json +22 -67
- package/dist/lib/actions.d.ts +0 -113
- package/dist/lib/handlers/index.d.ts +0 -0
- package/dist/lib/index.d.ts +0 -11
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/redaction-plugin.d.ts +0 -153
- package/dist/lib/reducer.d.ts +0 -6
- package/dist/lib/selectors.d.ts +0 -5
- package/dist/lib/tools.d.ts +0 -64
- package/dist/lib/types.d.ts +0 -142
- package/dist/preact/adapter.d.ts +0 -10
- 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 -520
- package/dist/preact/index.js.map +0 -1
- package/dist/preact/utils.d.ts +0 -1
- package/dist/react/adapter.d.ts +0 -2
- 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 -519
- package/dist/react/index.js.map +0 -1
- package/dist/react/utils.d.ts +0 -1
- package/dist/shared/components/annotations/index.d.ts +0 -2
- package/dist/shared/components/annotations/redact-area.d.ts +0 -18
- package/dist/shared/components/annotations/redact-highlight.d.ts +0 -18
- package/dist/shared/components/highlight.d.ts +0 -14
- package/dist/shared/components/index.d.ts +0 -5
- package/dist/shared/components/marquee-redact.d.ts +0 -15
- package/dist/shared/components/pending-redactions.d.ts +0 -12
- package/dist/shared/components/redact-renderer-registration.d.ts +0 -5
- package/dist/shared/components/redact-renderers.d.ts +0 -7
- package/dist/shared/components/redaction-layer.d.ts +0 -16
- package/dist/shared/components/selection-redact.d.ts +0 -7
- package/dist/shared/components/types.d.ts +0 -9
- package/dist/shared/hooks/index.d.ts +0 -1
- package/dist/shared/hooks/use-redaction.d.ts +0 -15
- package/dist/shared/index.d.ts +0 -4
- package/dist/shared-preact/components/annotations/index.d.ts +0 -2
- package/dist/shared-preact/components/annotations/redact-area.d.ts +0 -18
- package/dist/shared-preact/components/annotations/redact-highlight.d.ts +0 -18
- package/dist/shared-preact/components/highlight.d.ts +0 -14
- package/dist/shared-preact/components/index.d.ts +0 -5
- package/dist/shared-preact/components/marquee-redact.d.ts +0 -15
- package/dist/shared-preact/components/pending-redactions.d.ts +0 -12
- package/dist/shared-preact/components/redact-renderer-registration.d.ts +0 -5
- package/dist/shared-preact/components/redact-renderers.d.ts +0 -7
- package/dist/shared-preact/components/redaction-layer.d.ts +0 -16
- package/dist/shared-preact/components/selection-redact.d.ts +0 -7
- package/dist/shared-preact/components/types.d.ts +0 -9
- package/dist/shared-preact/hooks/index.d.ts +0 -1
- package/dist/shared-preact/hooks/use-redaction.d.ts +0 -15
- package/dist/shared-preact/index.d.ts +0 -4
- package/dist/shared-react/components/annotations/index.d.ts +0 -2
- package/dist/shared-react/components/annotations/redact-area.d.ts +0 -18
- package/dist/shared-react/components/annotations/redact-highlight.d.ts +0 -18
- package/dist/shared-react/components/highlight.d.ts +0 -14
- package/dist/shared-react/components/index.d.ts +0 -5
- package/dist/shared-react/components/marquee-redact.d.ts +0 -15
- package/dist/shared-react/components/pending-redactions.d.ts +0 -12
- package/dist/shared-react/components/redact-renderer-registration.d.ts +0 -5
- package/dist/shared-react/components/redact-renderers.d.ts +0 -7
- package/dist/shared-react/components/redaction-layer.d.ts +0 -16
- package/dist/shared-react/components/selection-redact.d.ts +0 -7
- package/dist/shared-react/components/types.d.ts +0 -9
- package/dist/shared-react/hooks/index.d.ts +0 -1
- package/dist/shared-react/hooks/use-redaction.d.ts +0 -15
- package/dist/shared-react/index.d.ts +0 -4
- package/dist/svelte/components/RedactRendererRegistration.svelte.d.ts +0 -7
- package/dist/svelte/components/annotations/RedactArea.svelte.d.ts +0 -5
- package/dist/svelte/components/annotations/RedactHighlight.svelte.d.ts +0 -5
- package/dist/svelte/components/annotations/index.d.ts +0 -2
- package/dist/svelte/components/highlight.svelte.d.ts +0 -14
- package/dist/svelte/components/index.d.ts +0 -8
- package/dist/svelte/components/marquee-redact.svelte.d.ts +0 -16
- package/dist/svelte/components/pending-redactions.svelte.d.ts +0 -15
- package/dist/svelte/components/redact-renderers.d.ts +0 -7
- package/dist/svelte/components/redaction-layer.svelte.d.ts +0 -20
- package/dist/svelte/components/selection-redact.svelte.d.ts +0 -8
- package/dist/svelte/hooks/index.d.ts +0 -1
- package/dist/svelte/hooks/use-redaction.svelte.d.ts +0 -21
- package/dist/svelte/index.cjs +0 -2
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.ts +0 -5
- package/dist/svelte/index.js +0 -801
- package/dist/svelte/index.js.map +0 -1
- package/dist/svelte/types.d.ts +0 -10
- package/dist/vue/components/annotations/index.d.ts +0 -2
- package/dist/vue/components/annotations/redact-area.vue.d.ts +0 -6
- package/dist/vue/components/annotations/redact-highlight.vue.d.ts +0 -6
- package/dist/vue/components/highlight.vue.d.ts +0 -17
- package/dist/vue/components/index.d.ts +0 -8
- package/dist/vue/components/marquee-redact.vue.d.ts +0 -19
- package/dist/vue/components/pending-redactions.vue.d.ts +0 -68
- package/dist/vue/components/redact-renderer-registration.vue.d.ts +0 -13
- package/dist/vue/components/redact-renderers.d.ts +0 -7
- package/dist/vue/components/redaction-layer.vue.d.ts +0 -50
- package/dist/vue/components/selection-redact.vue.d.ts +0 -8
- package/dist/vue/components/types.d.ts +0 -9
- package/dist/vue/hooks/index.d.ts +0 -1
- package/dist/vue/hooks/use-redaction.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 -5
- package/dist/vue/index.js +0 -638
- package/dist/vue/index.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1537 +1,225 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
1
|
+
import { createCapabilityToken, definePlugin } from "@embedpdf/core";
|
|
2
|
+
import { AnnotationToken } from "@embedpdf/plugin-annotation/internal";
|
|
3
|
+
import { InteractionToken } from "@embedpdf/plugin-interaction";
|
|
4
|
+
import { SelectionToken } from "@embedpdf/plugin-selection";
|
|
5
|
+
//#region src/capability.ts
|
|
6
|
+
/** Applying destroys content — its own granted power, narrower than annotate. */
|
|
7
|
+
const REDACT_CAPABILITY = "doc.redact";
|
|
8
|
+
/** The annotation plugin's model-id convention for a durable ref. */
|
|
9
|
+
const idOf = (ref) => ref.kind === "objectNumber" ? `obj:${ref.annotObjectNumber}` : ref.kind === "nm" ? `nm:${ref.nm}` : `idx:${ref.pageObjectNumber}:${ref.index}`;
|
|
10
|
+
/** The PDF-space regions a redact mark targets: its quads' boxes, else `/Rect`. */
|
|
11
|
+
function regionsOf(dto) {
|
|
12
|
+
if (dto.quadPoints.length === 0) return [dto.rect];
|
|
13
|
+
return dto.quadPoints.map((q) => {
|
|
14
|
+
const xs = [
|
|
15
|
+
q.p1.x,
|
|
16
|
+
q.p2.x,
|
|
17
|
+
q.p3.x,
|
|
18
|
+
q.p4.x
|
|
19
|
+
];
|
|
20
|
+
const ys = [
|
|
21
|
+
q.p1.y,
|
|
22
|
+
q.p2.y,
|
|
23
|
+
q.p3.y,
|
|
24
|
+
q.p4.y
|
|
25
|
+
];
|
|
26
|
+
return {
|
|
27
|
+
left: Math.min(...xs),
|
|
28
|
+
right: Math.max(...xs),
|
|
29
|
+
bottom: Math.min(...ys),
|
|
30
|
+
top: Math.max(...ys)
|
|
31
|
+
};
|
|
32
|
+
});
|
|
24
33
|
}
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
/** Positive-area intersection — the engine's collateral rule. */
|
|
35
|
+
const intersects = (a, b) => Math.min(a.right, b.right) > Math.max(a.left, b.left) && Math.min(a.top, b.top) > Math.max(a.bottom, b.bottom);
|
|
36
|
+
function createRedactionCapability(ctx) {
|
|
37
|
+
const anno = ctx.get(AnnotationToken);
|
|
38
|
+
const appliedCallbacks = /* @__PURE__ */ new Set();
|
|
39
|
+
const requireDoc = () => {
|
|
40
|
+
const doc = ctx.doc;
|
|
41
|
+
if (!doc) throw new Error("[redaction] no document bound");
|
|
42
|
+
return doc;
|
|
43
|
+
};
|
|
44
|
+
const requireService = () => {
|
|
45
|
+
const doc = requireDoc();
|
|
46
|
+
if (!doc.redaction) throw new Error("[redaction] engine has no redaction service");
|
|
47
|
+
return doc.redaction;
|
|
48
|
+
};
|
|
49
|
+
const pons = () => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);
|
|
50
|
+
const pageIndexOf = (pon) => ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.index ?? -1;
|
|
51
|
+
const pendingOn = (pon) => anno.list(pon).filter((a) => a.subtype === "redact");
|
|
52
|
+
const collectPending = () => {
|
|
53
|
+
const items = [];
|
|
54
|
+
for (const pon of pons()) for (const dto of pendingOn(pon)) items.push({
|
|
55
|
+
id: idOf(dto.ref),
|
|
56
|
+
ref: dto.ref,
|
|
57
|
+
pageObjectNumber: pon,
|
|
58
|
+
pageIndex: pageIndexOf(pon),
|
|
59
|
+
kind: dto.quadPoints.length > 0 ? "text" : "area",
|
|
60
|
+
overlayText: dto.overlayText
|
|
61
|
+
});
|
|
62
|
+
return items;
|
|
63
|
+
};
|
|
64
|
+
const fireApplied = (result) => {
|
|
65
|
+
for (const cb of appliedCallbacks) try {
|
|
66
|
+
cb(result);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
console.error("[redaction] onApplied callback failed", err);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const runApply = async (scope) => {
|
|
72
|
+
const service = requireService();
|
|
73
|
+
ctx.dispatch({ type: "APPLY_STARTED" });
|
|
74
|
+
try {
|
|
75
|
+
const result = await service.apply(scope);
|
|
76
|
+
ctx.dispatch({
|
|
77
|
+
type: "APPLY_FINISHED",
|
|
78
|
+
result
|
|
79
|
+
});
|
|
80
|
+
fireApplied(result);
|
|
81
|
+
return result;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
ctx.dispatch({
|
|
84
|
+
type: "APPLY_FINISHED",
|
|
85
|
+
result: null
|
|
86
|
+
});
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const doc = ctx.doc;
|
|
91
|
+
if (doc) ctx.cleanup(doc.events.subscribe((event) => {
|
|
92
|
+
if (event.type !== "redaction.applied") return;
|
|
93
|
+
if (event.origin.kind !== "remote") return;
|
|
94
|
+
ctx.dispatch({
|
|
95
|
+
type: "APPLY_FINISHED",
|
|
96
|
+
result: event
|
|
97
|
+
});
|
|
98
|
+
fireApplied(event);
|
|
99
|
+
}));
|
|
100
|
+
return {
|
|
101
|
+
canApply: () => (ctx.doc?.redaction !== void 0 && ctx.doc?.security.allows(REDACT_CAPABILITY)) ?? false,
|
|
102
|
+
isApplying: () => ctx.getState().applying,
|
|
103
|
+
lastResult: () => ctx.getState().lastResult,
|
|
104
|
+
enableRedact: () => ctx.tryGet(InteractionToken)?.activateTool("redact"),
|
|
105
|
+
toggleRedact: () => {
|
|
106
|
+
const interaction = ctx.tryGet(InteractionToken);
|
|
107
|
+
if (!interaction) return;
|
|
108
|
+
interaction.activateTool(interaction.activeToolId() === "redact" ? "pointer" : "redact");
|
|
109
|
+
},
|
|
110
|
+
isRedactActive: () => ctx.tryGet(InteractionToken)?.activeToolId() === "redact",
|
|
111
|
+
queueCurrentSelection: async () => {
|
|
112
|
+
const selection = ctx.tryGet(SelectionToken);
|
|
113
|
+
if (!selection || !selection.hasSelection()) return false;
|
|
114
|
+
const snapshot = selection.snapshot();
|
|
115
|
+
for (const page of snapshot.pages) {
|
|
116
|
+
if (page.rects.length === 0) continue;
|
|
117
|
+
anno.createMarkup("redact", page.pon, page.rects, "redact");
|
|
118
|
+
}
|
|
119
|
+
selection.clear();
|
|
120
|
+
return true;
|
|
121
|
+
},
|
|
122
|
+
preparePending: async () => {
|
|
123
|
+
await Promise.all(pons().map((pon) => anno.ensurePage(pon)));
|
|
124
|
+
},
|
|
125
|
+
getPending: collectPending,
|
|
126
|
+
pendingCount: () => collectPending().length,
|
|
127
|
+
estimateCollateral: (ids) => {
|
|
128
|
+
const wanted = ids ? new Set(ids) : null;
|
|
129
|
+
let count = 0;
|
|
130
|
+
for (const pon of pons()) {
|
|
131
|
+
const all = anno.list(pon);
|
|
132
|
+
const marks = all.filter((a) => a.subtype === "redact" && (!wanted || wanted.has(idOf(a.ref))));
|
|
133
|
+
if (marks.length === 0) continue;
|
|
134
|
+
const regions = marks.flatMap(regionsOf);
|
|
135
|
+
for (const other of all) {
|
|
136
|
+
if (other.subtype === "redact") continue;
|
|
137
|
+
if (regions.some((r) => intersects(r, other.rect))) count++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return count;
|
|
141
|
+
},
|
|
142
|
+
setLabel: async (ref, patch) => {
|
|
143
|
+
const current = anno.get(ref);
|
|
144
|
+
if (!current || current.subtype !== "redact") throw new Error("[redaction] setLabel target is not a redact annotation");
|
|
145
|
+
await anno.update(ref, {
|
|
146
|
+
subtype: "redact",
|
|
147
|
+
...patch.overlayText !== void 0 ? { overlayText: patch.overlayText } : {},
|
|
148
|
+
...patch.repeat !== void 0 ? { repeat: patch.repeat } : {},
|
|
149
|
+
fontFamily: current.fontFamily,
|
|
150
|
+
fontSize: current.fontSize,
|
|
151
|
+
fontColor: current.fontColor,
|
|
152
|
+
textAlign: current.textAlign
|
|
153
|
+
});
|
|
154
|
+
},
|
|
155
|
+
apply: async (ids) => {
|
|
156
|
+
const wanted = new Set(ids);
|
|
157
|
+
const refs = collectPending().filter((p) => wanted.has(p.id)).map((p) => p.ref);
|
|
158
|
+
if (refs.length === 0) throw new Error("[redaction] no matching pending redactions");
|
|
159
|
+
return runApply({
|
|
160
|
+
kind: "annotations",
|
|
161
|
+
refs
|
|
162
|
+
});
|
|
163
|
+
},
|
|
164
|
+
applyAll: async () => {
|
|
165
|
+
const pageObjectNumbers = pons();
|
|
166
|
+
if (pageObjectNumbers.length === 0) throw new Error("[redaction] document has no pages");
|
|
167
|
+
return runApply({
|
|
168
|
+
kind: "pages",
|
|
169
|
+
pageObjectNumbers
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
onApplied: (cb) => {
|
|
173
|
+
appliedCallbacks.add(cb);
|
|
174
|
+
return () => appliedCallbacks.delete(cb);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
27
177
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/reducer.ts
|
|
180
|
+
const initialRedactionState = () => ({
|
|
181
|
+
applying: false,
|
|
182
|
+
lastResult: null
|
|
31
183
|
});
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
const startRedaction = (documentId, mode) => ({
|
|
45
|
-
type: START_REDACTION,
|
|
46
|
-
payload: { documentId, mode }
|
|
47
|
-
});
|
|
48
|
-
const endRedaction = (documentId) => ({
|
|
49
|
-
type: END_REDACTION,
|
|
50
|
-
payload: documentId
|
|
51
|
-
});
|
|
52
|
-
const selectPending = (documentId, page, id) => ({
|
|
53
|
-
type: SELECT_PENDING,
|
|
54
|
-
payload: { documentId, page, id }
|
|
55
|
-
});
|
|
56
|
-
const deselectPending = (documentId) => ({
|
|
57
|
-
type: DESELECT_PENDING,
|
|
58
|
-
payload: documentId
|
|
59
|
-
});
|
|
60
|
-
const calculatePendingCount = (pending) => {
|
|
61
|
-
return Object.values(pending).reduce((total, items) => total + items.length, 0);
|
|
62
|
-
};
|
|
63
|
-
const initialDocumentState = {
|
|
64
|
-
isRedacting: false,
|
|
65
|
-
activeType: null,
|
|
66
|
-
pending: {},
|
|
67
|
-
pendingCount: 0,
|
|
68
|
-
selected: null
|
|
184
|
+
const redactionReducer = (state, action) => {
|
|
185
|
+
switch (action.type) {
|
|
186
|
+
case "APPLY_STARTED": return {
|
|
187
|
+
...state,
|
|
188
|
+
applying: true
|
|
189
|
+
};
|
|
190
|
+
case "APPLY_FINISHED": return {
|
|
191
|
+
applying: false,
|
|
192
|
+
lastResult: action.result ?? state.lastResult
|
|
193
|
+
};
|
|
194
|
+
default: return state;
|
|
195
|
+
}
|
|
69
196
|
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
case SET_ACTIVE_DOCUMENT: {
|
|
98
|
-
return {
|
|
99
|
-
...state,
|
|
100
|
-
activeDocumentId: action.payload
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
case ADD_PENDING: {
|
|
104
|
-
const { documentId, items } = action.payload;
|
|
105
|
-
const docState = state.documents[documentId];
|
|
106
|
-
if (!docState) return state;
|
|
107
|
-
const next = { ...docState.pending };
|
|
108
|
-
for (const item of items) {
|
|
109
|
-
const existing = next[item.page] ?? [];
|
|
110
|
-
if (existing.some((it) => it.id === item.id)) continue;
|
|
111
|
-
next[item.page] = existing.concat(item);
|
|
112
|
-
}
|
|
113
|
-
return {
|
|
114
|
-
...state,
|
|
115
|
-
documents: {
|
|
116
|
-
...state.documents,
|
|
117
|
-
[documentId]: {
|
|
118
|
-
...docState,
|
|
119
|
-
pending: next,
|
|
120
|
-
pendingCount: calculatePendingCount(next)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
case REMOVE_PENDING: {
|
|
126
|
-
const { documentId, page, id } = action.payload;
|
|
127
|
-
const docState = state.documents[documentId];
|
|
128
|
-
if (!docState) return state;
|
|
129
|
-
const list = docState.pending[page] ?? [];
|
|
130
|
-
const filtered = list.filter((it) => it.id !== id);
|
|
131
|
-
const next = { ...docState.pending, [page]: filtered };
|
|
132
|
-
const stillSelected = docState.selected && !(docState.selected.page === page && docState.selected.id === id);
|
|
133
|
-
return {
|
|
134
|
-
...state,
|
|
135
|
-
documents: {
|
|
136
|
-
...state.documents,
|
|
137
|
-
[documentId]: {
|
|
138
|
-
...docState,
|
|
139
|
-
pending: next,
|
|
140
|
-
pendingCount: calculatePendingCount(next),
|
|
141
|
-
selected: stillSelected ? docState.selected : null
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
case UPDATE_PENDING: {
|
|
147
|
-
const { documentId, page, id, patch } = action.payload;
|
|
148
|
-
const docState = state.documents[documentId];
|
|
149
|
-
if (!docState) return state;
|
|
150
|
-
const list = docState.pending[page] ?? [];
|
|
151
|
-
const updated = list.map((item) => item.id === id ? { ...item, ...patch } : item);
|
|
152
|
-
return {
|
|
153
|
-
...state,
|
|
154
|
-
documents: {
|
|
155
|
-
...state.documents,
|
|
156
|
-
[documentId]: {
|
|
157
|
-
...docState,
|
|
158
|
-
pending: { ...docState.pending, [page]: updated }
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
case CLEAR_PENDING: {
|
|
164
|
-
const documentId = action.payload;
|
|
165
|
-
const docState = state.documents[documentId];
|
|
166
|
-
if (!docState) return state;
|
|
167
|
-
return {
|
|
168
|
-
...state,
|
|
169
|
-
documents: {
|
|
170
|
-
...state.documents,
|
|
171
|
-
[documentId]: {
|
|
172
|
-
...docState,
|
|
173
|
-
pending: {},
|
|
174
|
-
pendingCount: 0,
|
|
175
|
-
selected: null
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
case SELECT_PENDING: {
|
|
181
|
-
const { documentId, page, id } = action.payload;
|
|
182
|
-
const docState = state.documents[documentId];
|
|
183
|
-
if (!docState) return state;
|
|
184
|
-
return {
|
|
185
|
-
...state,
|
|
186
|
-
documents: {
|
|
187
|
-
...state.documents,
|
|
188
|
-
[documentId]: {
|
|
189
|
-
...docState,
|
|
190
|
-
selected: { page, id }
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
case DESELECT_PENDING: {
|
|
196
|
-
const documentId = action.payload;
|
|
197
|
-
const docState = state.documents[documentId];
|
|
198
|
-
if (!docState) return state;
|
|
199
|
-
return {
|
|
200
|
-
...state,
|
|
201
|
-
documents: {
|
|
202
|
-
...state.documents,
|
|
203
|
-
[documentId]: {
|
|
204
|
-
...docState,
|
|
205
|
-
selected: null
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
case START_REDACTION: {
|
|
211
|
-
const { documentId, mode } = action.payload;
|
|
212
|
-
const docState = state.documents[documentId];
|
|
213
|
-
if (!docState) return state;
|
|
214
|
-
return {
|
|
215
|
-
...state,
|
|
216
|
-
documents: {
|
|
217
|
-
...state.documents,
|
|
218
|
-
[documentId]: {
|
|
219
|
-
...docState,
|
|
220
|
-
isRedacting: true,
|
|
221
|
-
activeType: mode
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
case END_REDACTION: {
|
|
227
|
-
const documentId = action.payload;
|
|
228
|
-
const docState = state.documents[documentId];
|
|
229
|
-
if (!docState) return state;
|
|
230
|
-
return {
|
|
231
|
-
...state,
|
|
232
|
-
documents: {
|
|
233
|
-
...state.documents,
|
|
234
|
-
[documentId]: {
|
|
235
|
-
...docState,
|
|
236
|
-
isRedacting: false,
|
|
237
|
-
activeType: null
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
case SET_ACTIVE_TYPE: {
|
|
243
|
-
const { documentId, mode } = action.payload;
|
|
244
|
-
const docState = state.documents[documentId];
|
|
245
|
-
if (!docState) return state;
|
|
246
|
-
return {
|
|
247
|
-
...state,
|
|
248
|
-
documents: {
|
|
249
|
-
...state.documents,
|
|
250
|
-
[documentId]: {
|
|
251
|
-
...docState,
|
|
252
|
-
activeType: mode
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
default:
|
|
258
|
-
return state;
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
const redactTool = defineAnnotationTool({
|
|
262
|
-
id: "redact",
|
|
263
|
-
name: "Redact",
|
|
264
|
-
categories: ["annotation", "redaction"],
|
|
265
|
-
matchScore: (a) => a.type === PdfAnnotationSubtype.REDACT ? 10 : 0,
|
|
266
|
-
interaction: {
|
|
267
|
-
mode: RedactionMode.Redact,
|
|
268
|
-
exclusive: false,
|
|
269
|
-
cursor: "crosshair",
|
|
270
|
-
textSelection: true,
|
|
271
|
-
// Dynamic based on whether it's a text or area redaction
|
|
272
|
-
isDraggable: (anno) => {
|
|
273
|
-
var _a;
|
|
274
|
-
if (anno.type !== PdfAnnotationSubtype.REDACT) return true;
|
|
275
|
-
return !((_a = anno.segmentRects) == null ? void 0 : _a.length);
|
|
276
|
-
},
|
|
277
|
-
isResizable: (anno) => {
|
|
278
|
-
var _a;
|
|
279
|
-
if (anno.type !== PdfAnnotationSubtype.REDACT) return true;
|
|
280
|
-
return !((_a = anno.segmentRects) == null ? void 0 : _a.length);
|
|
281
|
-
},
|
|
282
|
-
isRotatable: false,
|
|
283
|
-
lockAspectRatio: false,
|
|
284
|
-
isGroupDraggable: false,
|
|
285
|
-
isGroupResizable: false,
|
|
286
|
-
isGroupRotatable: false
|
|
287
|
-
},
|
|
288
|
-
defaults: {
|
|
289
|
-
type: PdfAnnotationSubtype.REDACT,
|
|
290
|
-
color: "#000000",
|
|
291
|
-
overlayColor: "#FFFFFF",
|
|
292
|
-
strokeColor: "#E44234",
|
|
293
|
-
opacity: 1
|
|
294
|
-
},
|
|
295
|
-
behavior: {
|
|
296
|
-
useAppearanceStream: false
|
|
297
|
-
}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/types.ts
|
|
199
|
+
const RedactionToken = createCapabilityToken("redaction");
|
|
200
|
+
//#endregion
|
|
201
|
+
//#region src/redaction.plugin.ts
|
|
202
|
+
/**
|
|
203
|
+
* Document-scoped redaction plugin — the DESTRUCTIVE half of the two-stage
|
|
204
|
+
* model. Marking is annotation-plane work (the composed `redact` tool ships
|
|
205
|
+
* with plugin-annotation); this plugin wraps `doc.redaction.apply`, projects
|
|
206
|
+
* the pending queue out of annotation state, and estimates collateral for
|
|
207
|
+
* confirm dialogs. It owns no mark state of its own.
|
|
208
|
+
*
|
|
209
|
+
* Trust boundary: on a layered document, applying rewrites the LAYER's bytes;
|
|
210
|
+
* the immutable base keeps the original. See the package README.
|
|
211
|
+
*/
|
|
212
|
+
const redactionPlugin = () => definePlugin({
|
|
213
|
+
id: "redaction",
|
|
214
|
+
token: RedactionToken,
|
|
215
|
+
scope: "document",
|
|
216
|
+
requires: [AnnotationToken],
|
|
217
|
+
optional: [InteractionToken, SelectionToken],
|
|
218
|
+
initialState: initialRedactionState,
|
|
219
|
+
reduce: redactionReducer,
|
|
220
|
+
capability: createRedactionCapability
|
|
298
221
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
super(id, registry);
|
|
304
|
-
this.redactionSelection$ = /* @__PURE__ */ new Map();
|
|
305
|
-
this.redactionMarquee$ = /* @__PURE__ */ new Map();
|
|
306
|
-
this.pending$ = createBehaviorEmitter();
|
|
307
|
-
this.selected$ = createBehaviorEmitter();
|
|
308
|
-
this.state$ = createBehaviorEmitter();
|
|
309
|
-
this.events$ = createBehaviorEmitter();
|
|
310
|
-
this.documentUnsubscribers = /* @__PURE__ */ new Map();
|
|
311
|
-
this.config = config;
|
|
312
|
-
this.selectionCapability = (_a = this.registry.getPlugin("selection")) == null ? void 0 : _a.provides();
|
|
313
|
-
this.interactionManagerCapability = (_b = this.registry.getPlugin("interaction-manager")) == null ? void 0 : _b.provides();
|
|
314
|
-
this.annotationCapability = (_c = this.registry.getPlugin("annotation")) == null ? void 0 : _c.provides();
|
|
315
|
-
this.historyCapability = (_d = this.registry.getPlugin("history")) == null ? void 0 : _d.provides();
|
|
316
|
-
if (this.config.useAnnotationMode) {
|
|
317
|
-
if (this.annotationCapability) {
|
|
318
|
-
this.useAnnotationMode = true;
|
|
319
|
-
} else {
|
|
320
|
-
this.logger.warn(
|
|
321
|
-
"RedactionPlugin",
|
|
322
|
-
"ConfigError",
|
|
323
|
-
"useAnnotationMode is enabled but annotation plugin is not available. Falling back to legacy mode."
|
|
324
|
-
);
|
|
325
|
-
this.useAnnotationMode = false;
|
|
326
|
-
}
|
|
327
|
-
} else {
|
|
328
|
-
this.useAnnotationMode = false;
|
|
329
|
-
}
|
|
330
|
-
if (this.useAnnotationMode && this.annotationCapability) {
|
|
331
|
-
for (const tool of redactTools) {
|
|
332
|
-
this.annotationCapability.addTool(tool);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
this.setupRedactionModes();
|
|
336
|
-
if (!this.useAnnotationMode && this.annotationCapability) {
|
|
337
|
-
this.logger.info(
|
|
338
|
-
"RedactionPlugin",
|
|
339
|
-
"LegacyMode",
|
|
340
|
-
"Using legacy redaction mode. Set useAnnotationMode: true in config to use annotation-based redactions."
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
this.setupModeChangeListener();
|
|
344
|
-
}
|
|
345
|
-
/**
|
|
346
|
-
* Setup redaction modes - registers all interaction modes for redaction.
|
|
347
|
-
* Works for both annotation mode and legacy mode.
|
|
348
|
-
*/
|
|
349
|
-
setupRedactionModes() {
|
|
350
|
-
if (!this.interactionManagerCapability) return;
|
|
351
|
-
this.interactionManagerCapability.registerMode({
|
|
352
|
-
id: RedactionMode.Redact,
|
|
353
|
-
scope: "page",
|
|
354
|
-
exclusive: false,
|
|
355
|
-
cursor: "crosshair"
|
|
356
|
-
});
|
|
357
|
-
this.interactionManagerCapability.registerMode({
|
|
358
|
-
id: RedactionMode.MarqueeRedact,
|
|
359
|
-
scope: "page",
|
|
360
|
-
exclusive: false,
|
|
361
|
-
cursor: "crosshair"
|
|
362
|
-
});
|
|
363
|
-
this.interactionManagerCapability.registerMode({
|
|
364
|
-
id: RedactionMode.RedactSelection,
|
|
365
|
-
scope: "page",
|
|
366
|
-
exclusive: false
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
/**
|
|
370
|
-
* Setup mode change listener - handles all redaction modes
|
|
371
|
-
*/
|
|
372
|
-
setupModeChangeListener() {
|
|
373
|
-
var _a;
|
|
374
|
-
(_a = this.interactionManagerCapability) == null ? void 0 : _a.onModeChange((modeState) => {
|
|
375
|
-
const documentId = modeState.documentId;
|
|
376
|
-
const isRedactionMode = modeState.activeMode === RedactionMode.Redact || modeState.activeMode === RedactionMode.MarqueeRedact || modeState.activeMode === RedactionMode.RedactSelection;
|
|
377
|
-
if (isRedactionMode) {
|
|
378
|
-
this.dispatch(startRedaction(documentId, modeState.activeMode));
|
|
379
|
-
} else {
|
|
380
|
-
const docState = this.getDocumentState(documentId);
|
|
381
|
-
if (docState == null ? void 0 : docState.isRedacting) {
|
|
382
|
-
this.dispatch(endRedaction(documentId));
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
// ─────────────────────────────────────────────────────────
|
|
388
|
-
// Document Lifecycle Hooks (from BasePlugin)
|
|
389
|
-
// ─────────────────────────────────────────────────────────
|
|
390
|
-
onDocumentLoadingStarted(documentId) {
|
|
391
|
-
this.dispatch(
|
|
392
|
-
initRedactionState(documentId, {
|
|
393
|
-
...initialDocumentState
|
|
394
|
-
})
|
|
395
|
-
);
|
|
396
|
-
this.redactionSelection$.set(documentId, createBehaviorEmitter());
|
|
397
|
-
this.redactionMarquee$.set(
|
|
398
|
-
documentId,
|
|
399
|
-
createBehaviorEmitter()
|
|
400
|
-
);
|
|
401
|
-
const unsubscribers = [];
|
|
402
|
-
if (this.selectionCapability) {
|
|
403
|
-
const selectionScope = this.selectionCapability.forDocument(documentId);
|
|
404
|
-
const unsubSelection = selectionScope.onSelectionChange(() => {
|
|
405
|
-
const docState = this.getDocumentState(documentId);
|
|
406
|
-
if (!(docState == null ? void 0 : docState.isRedacting)) return;
|
|
407
|
-
const formattedSelection = selectionScope.getFormattedSelection();
|
|
408
|
-
const emitter = this.redactionSelection$.get(documentId);
|
|
409
|
-
emitter == null ? void 0 : emitter.emit(formattedSelection);
|
|
410
|
-
});
|
|
411
|
-
const unsubEndSelection = selectionScope.onEndSelection(() => {
|
|
412
|
-
const docState = this.getDocumentState(documentId);
|
|
413
|
-
if (!(docState == null ? void 0 : docState.isRedacting)) return;
|
|
414
|
-
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyContents)) return;
|
|
415
|
-
const formattedSelection = selectionScope.getFormattedSelection();
|
|
416
|
-
if (!formattedSelection.length) return;
|
|
417
|
-
const textTask = selectionScope.getSelectedText();
|
|
418
|
-
const emitter = this.redactionSelection$.get(documentId);
|
|
419
|
-
emitter == null ? void 0 : emitter.emit([]);
|
|
420
|
-
selectionScope.clear();
|
|
421
|
-
textTask.wait(
|
|
422
|
-
(textArr) => {
|
|
423
|
-
const text = textArr.join(" ");
|
|
424
|
-
this.createRedactionsFromSelection(documentId, formattedSelection, text);
|
|
425
|
-
},
|
|
426
|
-
() => {
|
|
427
|
-
this.createRedactionsFromSelection(documentId, formattedSelection);
|
|
428
|
-
}
|
|
429
|
-
);
|
|
430
|
-
});
|
|
431
|
-
const unsubMarqueeChange = selectionScope.onMarqueeChange((event) => {
|
|
432
|
-
var _a;
|
|
433
|
-
const docState = this.getDocumentState(documentId);
|
|
434
|
-
if (!(docState == null ? void 0 : docState.isRedacting)) return;
|
|
435
|
-
(_a = this.redactionMarquee$.get(documentId)) == null ? void 0 : _a.emit({ pageIndex: event.pageIndex, rect: event.rect, modeId: event.modeId });
|
|
436
|
-
});
|
|
437
|
-
const unsubMarqueeEnd = selectionScope.onMarqueeEnd((event) => {
|
|
438
|
-
const docState = this.getDocumentState(documentId);
|
|
439
|
-
if (!(docState == null ? void 0 : docState.isRedacting)) return;
|
|
440
|
-
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyContents)) return;
|
|
441
|
-
if (this.useAnnotationMode) {
|
|
442
|
-
this.createRedactAnnotationFromArea(documentId, event.pageIndex, event.rect);
|
|
443
|
-
} else {
|
|
444
|
-
const redactionColor = this.config.drawBlackBoxes ? "#000000" : "transparent";
|
|
445
|
-
const item = {
|
|
446
|
-
id: uuidV4(),
|
|
447
|
-
kind: "area",
|
|
448
|
-
page: event.pageIndex,
|
|
449
|
-
rect: event.rect,
|
|
450
|
-
source: "legacy",
|
|
451
|
-
markColor: "#FF0000",
|
|
452
|
-
redactionColor
|
|
453
|
-
};
|
|
454
|
-
this.dispatch(addPending(documentId, [item]));
|
|
455
|
-
this.selectPending(event.pageIndex, item.id, documentId);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
458
|
-
const unsubEmptySpaceClick = selectionScope.onEmptySpaceClick(() => {
|
|
459
|
-
this.deselectPending(documentId);
|
|
460
|
-
});
|
|
461
|
-
unsubscribers.push(
|
|
462
|
-
unsubSelection,
|
|
463
|
-
unsubEndSelection,
|
|
464
|
-
unsubMarqueeChange,
|
|
465
|
-
unsubMarqueeEnd,
|
|
466
|
-
unsubEmptySpaceClick
|
|
467
|
-
);
|
|
468
|
-
}
|
|
469
|
-
if (this.useAnnotationMode && this.annotationCapability) {
|
|
470
|
-
const annoScope = this.annotationCapability.forDocument(documentId);
|
|
471
|
-
const unsubEvents = annoScope.onAnnotationEvent((event) => {
|
|
472
|
-
var _a;
|
|
473
|
-
if (event.type === "loaded") {
|
|
474
|
-
this.syncFromAnnotationLoad(documentId);
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
if (((_a = event.annotation) == null ? void 0 : _a.type) !== PdfAnnotationSubtype.REDACT) return;
|
|
478
|
-
const redactAnno = event.annotation;
|
|
479
|
-
if (event.type === "create") {
|
|
480
|
-
this.syncFromAnnotationCreate(documentId, redactAnno);
|
|
481
|
-
this.events$.emit({
|
|
482
|
-
type: "add",
|
|
483
|
-
documentId,
|
|
484
|
-
items: [this.annotationToRedactionItem(redactAnno)]
|
|
485
|
-
});
|
|
486
|
-
} else if (event.type === "update") {
|
|
487
|
-
this.logger.debug("RedactionPlugin", "AnnotationUpdated", {
|
|
488
|
-
documentId,
|
|
489
|
-
redactAnno,
|
|
490
|
-
patch: event.patch
|
|
491
|
-
});
|
|
492
|
-
this.syncFromAnnotationUpdate(
|
|
493
|
-
documentId,
|
|
494
|
-
redactAnno,
|
|
495
|
-
event.patch
|
|
496
|
-
);
|
|
497
|
-
} else if (event.type === "delete") {
|
|
498
|
-
this.syncFromAnnotationDelete(documentId, redactAnno);
|
|
499
|
-
this.events$.emit({
|
|
500
|
-
type: "remove",
|
|
501
|
-
documentId,
|
|
502
|
-
page: redactAnno.pageIndex,
|
|
503
|
-
id: redactAnno.id
|
|
504
|
-
});
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
const unsubState = annoScope.onStateChange(() => {
|
|
508
|
-
this.syncSelectionFromAnnotation(documentId);
|
|
509
|
-
});
|
|
510
|
-
unsubscribers.push(unsubEvents, unsubState);
|
|
511
|
-
}
|
|
512
|
-
this.documentUnsubscribers.set(documentId, unsubscribers);
|
|
513
|
-
this.logger.debug(
|
|
514
|
-
"RedactionPlugin",
|
|
515
|
-
"DocumentOpened",
|
|
516
|
-
`Initialized redaction state for document: ${documentId}`
|
|
517
|
-
);
|
|
518
|
-
}
|
|
519
|
-
onDocumentLoaded(documentId) {
|
|
520
|
-
var _a, _b, _c;
|
|
521
|
-
(_a = this.selectionCapability) == null ? void 0 : _a.enableForMode(
|
|
522
|
-
RedactionMode.Redact,
|
|
523
|
-
{
|
|
524
|
-
enableSelection: true,
|
|
525
|
-
showSelectionRects: false,
|
|
526
|
-
enableMarquee: true,
|
|
527
|
-
showMarqueeRects: false
|
|
528
|
-
},
|
|
529
|
-
documentId
|
|
530
|
-
);
|
|
531
|
-
(_b = this.selectionCapability) == null ? void 0 : _b.enableForMode(
|
|
532
|
-
RedactionMode.MarqueeRedact,
|
|
533
|
-
{
|
|
534
|
-
enableSelection: false,
|
|
535
|
-
enableMarquee: true,
|
|
536
|
-
showMarqueeRects: false
|
|
537
|
-
},
|
|
538
|
-
documentId
|
|
539
|
-
);
|
|
540
|
-
(_c = this.selectionCapability) == null ? void 0 : _c.enableForMode(
|
|
541
|
-
RedactionMode.RedactSelection,
|
|
542
|
-
{
|
|
543
|
-
enableSelection: true,
|
|
544
|
-
showSelectionRects: false
|
|
545
|
-
},
|
|
546
|
-
documentId
|
|
547
|
-
);
|
|
548
|
-
}
|
|
549
|
-
onDocumentClosed(documentId) {
|
|
550
|
-
this.dispatch(cleanupRedactionState(documentId));
|
|
551
|
-
const emitter = this.redactionSelection$.get(documentId);
|
|
552
|
-
emitter == null ? void 0 : emitter.clear();
|
|
553
|
-
this.redactionSelection$.delete(documentId);
|
|
554
|
-
const marqueeEmitter = this.redactionMarquee$.get(documentId);
|
|
555
|
-
marqueeEmitter == null ? void 0 : marqueeEmitter.clear();
|
|
556
|
-
this.redactionMarquee$.delete(documentId);
|
|
557
|
-
const unsubscribers = this.documentUnsubscribers.get(documentId);
|
|
558
|
-
if (unsubscribers) {
|
|
559
|
-
unsubscribers.forEach((unsub) => unsub());
|
|
560
|
-
this.documentUnsubscribers.delete(documentId);
|
|
561
|
-
}
|
|
562
|
-
this.logger.debug(
|
|
563
|
-
"RedactionPlugin",
|
|
564
|
-
"DocumentClosed",
|
|
565
|
-
`Cleaned up redaction state for document: ${documentId}`
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
// ─────────────────────────────────────────────────────────
|
|
569
|
-
// Capability
|
|
570
|
-
// ─────────────────────────────────────────────────────────
|
|
571
|
-
async initialize(_config) {
|
|
572
|
-
this.logger.info("RedactionPlugin", "Initialize", "Redaction plugin initialized");
|
|
573
|
-
}
|
|
574
|
-
buildCapability() {
|
|
575
|
-
return {
|
|
576
|
-
// Active document operations
|
|
577
|
-
queueCurrentSelectionAsPending: () => this.queueCurrentSelectionAsPending(),
|
|
578
|
-
// Unified redact mode
|
|
579
|
-
enableRedact: () => this.enableRedact(),
|
|
580
|
-
toggleRedact: () => this.toggleRedact(),
|
|
581
|
-
isRedactActive: () => this.isRedactActive(),
|
|
582
|
-
endRedact: () => this.endRedact(),
|
|
583
|
-
// Legacy marquee mode
|
|
584
|
-
enableMarqueeRedact: () => this.enableMarqueeRedact(),
|
|
585
|
-
toggleMarqueeRedact: () => this.toggleMarqueeRedact(),
|
|
586
|
-
isMarqueeRedactActive: () => this.isMarqueeRedactActive(),
|
|
587
|
-
// Legacy selection mode
|
|
588
|
-
enableRedactSelection: () => this.enableRedactSelection(),
|
|
589
|
-
toggleRedactSelection: () => this.toggleRedactSelection(),
|
|
590
|
-
isRedactSelectionActive: () => this.isRedactSelectionActive(),
|
|
591
|
-
addPending: (items) => this.addPendingItems(items),
|
|
592
|
-
removePending: (page, id) => this.removePendingItem(page, id),
|
|
593
|
-
clearPending: () => this.clearPendingItems(),
|
|
594
|
-
commitAllPending: () => this.commitAllPending(),
|
|
595
|
-
commitPending: (page, id) => this.commitPendingOne(page, id),
|
|
596
|
-
selectPending: (page, id) => this.selectPending(page, id),
|
|
597
|
-
getSelectedPending: () => this.getSelectedPending(),
|
|
598
|
-
deselectPending: () => this.deselectPending(),
|
|
599
|
-
getState: () => this.getDocumentStateOrThrow(),
|
|
600
|
-
// Document-scoped operations
|
|
601
|
-
forDocument: (documentId) => this.createRedactionScope(documentId),
|
|
602
|
-
// Events
|
|
603
|
-
onPendingChange: this.pending$.on,
|
|
604
|
-
onSelectedChange: this.selected$.on,
|
|
605
|
-
onRedactionEvent: this.events$.on,
|
|
606
|
-
onStateChange: this.state$.on
|
|
607
|
-
};
|
|
608
|
-
}
|
|
609
|
-
// ─────────────────────────────────────────────────────────
|
|
610
|
-
// Document Scoping
|
|
611
|
-
// ─────────────────────────────────────────────────────────
|
|
612
|
-
createRedactionScope(documentId) {
|
|
613
|
-
return {
|
|
614
|
-
queueCurrentSelectionAsPending: () => this.queueCurrentSelectionAsPending(documentId),
|
|
615
|
-
// Unified redact mode
|
|
616
|
-
enableRedact: () => this.enableRedact(documentId),
|
|
617
|
-
toggleRedact: () => this.toggleRedact(documentId),
|
|
618
|
-
isRedactActive: () => this.isRedactActive(documentId),
|
|
619
|
-
endRedact: () => this.endRedact(documentId),
|
|
620
|
-
// Legacy marquee mode
|
|
621
|
-
enableMarqueeRedact: () => this.enableMarqueeRedact(documentId),
|
|
622
|
-
toggleMarqueeRedact: () => this.toggleMarqueeRedact(documentId),
|
|
623
|
-
isMarqueeRedactActive: () => this.isMarqueeRedactActive(documentId),
|
|
624
|
-
// Legacy selection mode
|
|
625
|
-
enableRedactSelection: () => this.enableRedactSelection(documentId),
|
|
626
|
-
toggleRedactSelection: () => this.toggleRedactSelection(documentId),
|
|
627
|
-
isRedactSelectionActive: () => this.isRedactSelectionActive(documentId),
|
|
628
|
-
addPending: (items) => this.addPendingItems(items, documentId),
|
|
629
|
-
removePending: (page, id) => this.removePendingItem(page, id, documentId),
|
|
630
|
-
clearPending: () => this.clearPendingItems(documentId),
|
|
631
|
-
commitAllPending: () => this.commitAllPending(documentId),
|
|
632
|
-
commitPending: (page, id) => this.commitPendingOne(page, id, documentId),
|
|
633
|
-
selectPending: (page, id) => this.selectPending(page, id, documentId),
|
|
634
|
-
getSelectedPending: () => this.getSelectedPending(documentId),
|
|
635
|
-
deselectPending: () => this.deselectPending(documentId),
|
|
636
|
-
getState: () => this.getDocumentStateOrThrow(documentId),
|
|
637
|
-
onPendingChange: (listener) => this.pending$.on((event) => {
|
|
638
|
-
if (event.documentId === documentId) listener(event.pending);
|
|
639
|
-
}),
|
|
640
|
-
onSelectedChange: (listener) => this.selected$.on((event) => {
|
|
641
|
-
if (event.documentId === documentId) listener(event.selected);
|
|
642
|
-
}),
|
|
643
|
-
onRedactionEvent: (listener) => this.events$.on((event) => {
|
|
644
|
-
if (event.documentId === documentId) listener(event);
|
|
645
|
-
}),
|
|
646
|
-
onStateChange: (listener) => this.state$.on((event) => {
|
|
647
|
-
if (event.documentId === documentId) listener(event.state);
|
|
648
|
-
})
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
// ─────────────────────────────────────────────────────────
|
|
652
|
-
// State Helpers
|
|
653
|
-
// ─────────────────────────────────────────────────────────
|
|
654
|
-
/**
|
|
655
|
-
* Get pending redactions derived from annotation plugin (annotation mode only)
|
|
656
|
-
*/
|
|
657
|
-
getPendingFromAnnotations(documentId) {
|
|
658
|
-
if (!this.annotationCapability) return {};
|
|
659
|
-
try {
|
|
660
|
-
const annoState = this.annotationCapability.forDocument(documentId).getState();
|
|
661
|
-
const result = {};
|
|
662
|
-
for (const ta of Object.values(annoState.byUid)) {
|
|
663
|
-
if (ta.object.type === PdfAnnotationSubtype.REDACT) {
|
|
664
|
-
const item = this.annotationToRedactionItem(ta.object);
|
|
665
|
-
const page = ta.object.pageIndex;
|
|
666
|
-
(result[page] ?? (result[page] = [])).push(item);
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
return result;
|
|
670
|
-
} catch {
|
|
671
|
-
return {};
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
getDocumentState(documentId) {
|
|
675
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
676
|
-
return this.state.documents[id] ?? null;
|
|
677
|
-
}
|
|
678
|
-
getDocumentStateOrThrow(documentId) {
|
|
679
|
-
const state = this.getDocumentState(documentId);
|
|
680
|
-
if (!state) {
|
|
681
|
-
throw new Error(`Redaction state not found for document: ${documentId ?? "active"}`);
|
|
682
|
-
}
|
|
683
|
-
return state;
|
|
684
|
-
}
|
|
685
|
-
isRedactTool(tool) {
|
|
686
|
-
return (tool == null ? void 0 : tool.id) === redactTool.id && tool.defaults.type === PdfAnnotationSubtype.REDACT;
|
|
687
|
-
}
|
|
688
|
-
getRedactTool() {
|
|
689
|
-
var _a;
|
|
690
|
-
const tool = (_a = this.annotationCapability) == null ? void 0 : _a.getTool("redact");
|
|
691
|
-
return this.isRedactTool(tool) ? tool : void 0;
|
|
692
|
-
}
|
|
693
|
-
// ─────────────────────────────────────────────────────────
|
|
694
|
-
// Annotation Mode State Sync
|
|
695
|
-
// ─────────────────────────────────────────────────────────
|
|
696
|
-
/**
|
|
697
|
-
* Sync internal state when REDACT annotation is created.
|
|
698
|
-
* Called from annotation event listener in annotation mode.
|
|
699
|
-
*/
|
|
700
|
-
syncFromAnnotationCreate(documentId, annotation) {
|
|
701
|
-
const item = this.annotationToRedactionItem(annotation);
|
|
702
|
-
this.dispatch(addPending(documentId, [item]));
|
|
703
|
-
}
|
|
704
|
-
/**
|
|
705
|
-
* Sync internal state when REDACT annotation is updated (moved/resized/color changed).
|
|
706
|
-
* Called from annotation event listener in annotation mode.
|
|
707
|
-
*/
|
|
708
|
-
syncFromAnnotationUpdate(documentId, annotation, patch) {
|
|
709
|
-
if (!("rect" in patch) && !("segmentRects" in patch) && !("strokeColor" in patch) && !("color" in patch))
|
|
710
|
-
return;
|
|
711
|
-
const updatePatch = {};
|
|
712
|
-
if (patch.rect) updatePatch.rect = patch.rect;
|
|
713
|
-
if (patch.segmentRects) updatePatch.rects = patch.segmentRects;
|
|
714
|
-
if (patch.strokeColor) updatePatch.markColor = patch.strokeColor;
|
|
715
|
-
if (patch.color) updatePatch.redactionColor = patch.color;
|
|
716
|
-
this.logger.debug("RedactionPlugin", "AnnotationUpdated", {
|
|
717
|
-
documentId,
|
|
718
|
-
annotation,
|
|
719
|
-
patch: updatePatch
|
|
720
|
-
});
|
|
721
|
-
this.dispatch(updatePending(documentId, annotation.pageIndex, annotation.id, updatePatch));
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Sync internal state when REDACT annotation is deleted.
|
|
725
|
-
* Called from annotation event listener in annotation mode.
|
|
726
|
-
*/
|
|
727
|
-
syncFromAnnotationDelete(documentId, annotation) {
|
|
728
|
-
this.dispatch(removePending(documentId, annotation.pageIndex, annotation.id));
|
|
729
|
-
}
|
|
730
|
-
/**
|
|
731
|
-
* Sync internal state from existing REDACT annotations after initial load.
|
|
732
|
-
* Called when annotation plugin emits 'loaded' event.
|
|
733
|
-
*/
|
|
734
|
-
syncFromAnnotationLoad(documentId) {
|
|
735
|
-
const pending = this.getPendingFromAnnotations(documentId);
|
|
736
|
-
this.dispatch(clearPending(documentId));
|
|
737
|
-
for (const [, items] of Object.entries(pending)) {
|
|
738
|
-
if (items.length > 0) {
|
|
739
|
-
this.dispatch(addPending(documentId, items));
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* Sync selection state from annotation plugin's selected REDACT annotation.
|
|
745
|
-
* Called when annotation plugin state changes.
|
|
746
|
-
*/
|
|
747
|
-
syncSelectionFromAnnotation(documentId) {
|
|
748
|
-
var _a;
|
|
749
|
-
const annoState = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(documentId).getState();
|
|
750
|
-
if (!annoState) return;
|
|
751
|
-
const selectedRedact = annoState.selectedUids.map((uid) => annoState.byUid[uid]).find((ta) => (ta == null ? void 0 : ta.object.type) === PdfAnnotationSubtype.REDACT);
|
|
752
|
-
if (selectedRedact) {
|
|
753
|
-
const obj = selectedRedact.object;
|
|
754
|
-
this.dispatch(selectPending(documentId, obj.pageIndex, obj.id));
|
|
755
|
-
} else {
|
|
756
|
-
const docState = this.getDocumentState(documentId);
|
|
757
|
-
if (docState == null ? void 0 : docState.selected) {
|
|
758
|
-
this.dispatch(deselectPending(documentId));
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// ─────────────────────────────────────────────────────────
|
|
763
|
-
// Core Operations
|
|
764
|
-
// ─────────────────────────────────────────────────────────
|
|
765
|
-
addPendingItems(items, documentId) {
|
|
766
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
767
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
768
|
-
this.logger.debug(
|
|
769
|
-
"RedactionPlugin",
|
|
770
|
-
"AddPendingItems",
|
|
771
|
-
`Cannot add redactions: document ${id} lacks ModifyContents permission`
|
|
772
|
-
);
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
if (this.useAnnotationMode) {
|
|
776
|
-
if (!this.annotationCapability) return;
|
|
777
|
-
const annoScope = this.annotationCapability.forDocument(id);
|
|
778
|
-
for (const item of items) {
|
|
779
|
-
const annotation = this.redactionItemToAnnotation(item);
|
|
780
|
-
annoScope.createAnnotation(item.page, annotation);
|
|
781
|
-
}
|
|
782
|
-
if (items.length > 0) {
|
|
783
|
-
const lastItem = items[items.length - 1];
|
|
784
|
-
annoScope.selectAnnotation(lastItem.page, lastItem.id);
|
|
785
|
-
}
|
|
786
|
-
} else {
|
|
787
|
-
this.dispatch(addPending(id, items));
|
|
788
|
-
}
|
|
789
|
-
this.events$.emit({ type: "add", documentId: id, items });
|
|
790
|
-
}
|
|
791
|
-
removePendingItem(page, itemId, documentId) {
|
|
792
|
-
var _a;
|
|
793
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
794
|
-
if (this.useAnnotationMode) {
|
|
795
|
-
(_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id).deleteAnnotation(page, itemId);
|
|
796
|
-
} else {
|
|
797
|
-
this.dispatch(removePending(id, page, itemId));
|
|
798
|
-
}
|
|
799
|
-
this.events$.emit({ type: "remove", documentId: id, page, id: itemId });
|
|
800
|
-
}
|
|
801
|
-
clearPendingItems(documentId) {
|
|
802
|
-
var _a;
|
|
803
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
804
|
-
if (this.useAnnotationMode) {
|
|
805
|
-
const pending = this.getPendingFromAnnotations(id);
|
|
806
|
-
const annoScope = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id);
|
|
807
|
-
for (const [pageStr, items] of Object.entries(pending)) {
|
|
808
|
-
const page = Number(pageStr);
|
|
809
|
-
for (const item of items) {
|
|
810
|
-
annoScope == null ? void 0 : annoScope.deleteAnnotation(page, item.id);
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
} else {
|
|
814
|
-
this.dispatch(clearPending(id));
|
|
815
|
-
}
|
|
816
|
-
this.events$.emit({ type: "clear", documentId: id });
|
|
817
|
-
}
|
|
818
|
-
selectPending(page, itemId, documentId) {
|
|
819
|
-
var _a, _b, _c;
|
|
820
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
821
|
-
if (this.useAnnotationMode) {
|
|
822
|
-
(_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id).selectAnnotation(page, itemId);
|
|
823
|
-
} else {
|
|
824
|
-
this.dispatch(selectPending(id, page, itemId));
|
|
825
|
-
(_b = this.interactionManagerCapability) == null ? void 0 : _b.claimPageActivity(id, "redaction-selection", page);
|
|
826
|
-
}
|
|
827
|
-
(_c = this.selectionCapability) == null ? void 0 : _c.forDocument(id).clear();
|
|
828
|
-
}
|
|
829
|
-
getSelectedPending(documentId) {
|
|
830
|
-
var _a;
|
|
831
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
832
|
-
return ((_a = this.getDocumentState(id)) == null ? void 0 : _a.selected) ?? null;
|
|
833
|
-
}
|
|
834
|
-
deselectPending(documentId) {
|
|
835
|
-
var _a, _b;
|
|
836
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
837
|
-
if (this.useAnnotationMode) {
|
|
838
|
-
(_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id).deselectAnnotation();
|
|
839
|
-
} else {
|
|
840
|
-
this.dispatch(deselectPending(id));
|
|
841
|
-
(_b = this.interactionManagerCapability) == null ? void 0 : _b.releasePageActivity(id, "redaction-selection");
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
// ─────────────────────────────────────────────────────────
|
|
845
|
-
// Legacy Selection Mode (text-based redactions)
|
|
846
|
-
// ─────────────────────────────────────────────────────────
|
|
847
|
-
enableRedactSelection(documentId) {
|
|
848
|
-
var _a;
|
|
849
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
850
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
851
|
-
this.logger.debug(
|
|
852
|
-
"RedactionPlugin",
|
|
853
|
-
"EnableRedactSelection",
|
|
854
|
-
`Cannot enable redact selection: document ${id} lacks ModifyContents permission`
|
|
855
|
-
);
|
|
856
|
-
return;
|
|
857
|
-
}
|
|
858
|
-
(_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.RedactSelection);
|
|
859
|
-
}
|
|
860
|
-
toggleRedactSelection(documentId) {
|
|
861
|
-
var _a;
|
|
862
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
863
|
-
const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
|
|
864
|
-
const activeMode = scope == null ? void 0 : scope.getActiveMode();
|
|
865
|
-
if (activeMode === RedactionMode.RedactSelection) {
|
|
866
|
-
scope == null ? void 0 : scope.activateDefaultMode();
|
|
867
|
-
} else {
|
|
868
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
|
-
scope == null ? void 0 : scope.activate(RedactionMode.RedactSelection);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
isRedactSelectionActive(documentId) {
|
|
875
|
-
var _a;
|
|
876
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
877
|
-
const activeMode = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode();
|
|
878
|
-
return activeMode === RedactionMode.Redact || activeMode === RedactionMode.RedactSelection;
|
|
879
|
-
}
|
|
880
|
-
// ─────────────────────────────────────────────────────────
|
|
881
|
-
// Legacy Marquee Mode (area-based redactions)
|
|
882
|
-
// ─────────────────────────────────────────────────────────
|
|
883
|
-
enableMarqueeRedact(documentId) {
|
|
884
|
-
var _a;
|
|
885
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
886
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
887
|
-
this.logger.debug(
|
|
888
|
-
"RedactionPlugin",
|
|
889
|
-
"EnableMarqueeRedact",
|
|
890
|
-
`Cannot enable marquee redact: document ${id} lacks ModifyContents permission`
|
|
891
|
-
);
|
|
892
|
-
return;
|
|
893
|
-
}
|
|
894
|
-
(_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.MarqueeRedact);
|
|
895
|
-
}
|
|
896
|
-
toggleMarqueeRedact(documentId) {
|
|
897
|
-
var _a;
|
|
898
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
899
|
-
const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
|
|
900
|
-
const activeMode = scope == null ? void 0 : scope.getActiveMode();
|
|
901
|
-
if (activeMode === RedactionMode.MarqueeRedact) {
|
|
902
|
-
scope == null ? void 0 : scope.activateDefaultMode();
|
|
903
|
-
} else {
|
|
904
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
905
|
-
return;
|
|
906
|
-
}
|
|
907
|
-
scope == null ? void 0 : scope.activate(RedactionMode.MarqueeRedact);
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
isMarqueeRedactActive(documentId) {
|
|
911
|
-
var _a;
|
|
912
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
913
|
-
const activeMode = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode();
|
|
914
|
-
return activeMode === RedactionMode.Redact || activeMode === RedactionMode.MarqueeRedact;
|
|
915
|
-
}
|
|
916
|
-
// ─────────────────────────────────────────────────────────
|
|
917
|
-
// Unified Redact Mode (recommended)
|
|
918
|
-
// ─────────────────────────────────────────────────────────
|
|
919
|
-
enableRedact(documentId) {
|
|
920
|
-
var _a;
|
|
921
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
922
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
923
|
-
this.logger.debug(
|
|
924
|
-
"RedactionPlugin",
|
|
925
|
-
"EnableRedact",
|
|
926
|
-
`Cannot enable redact mode: document ${id} lacks ModifyContents permission`
|
|
927
|
-
);
|
|
928
|
-
return;
|
|
929
|
-
}
|
|
930
|
-
(_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.Redact);
|
|
931
|
-
}
|
|
932
|
-
toggleRedact(documentId) {
|
|
933
|
-
var _a;
|
|
934
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
935
|
-
const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
|
|
936
|
-
const activeMode = scope == null ? void 0 : scope.getActiveMode();
|
|
937
|
-
if (activeMode === RedactionMode.Redact) {
|
|
938
|
-
scope == null ? void 0 : scope.activateDefaultMode();
|
|
939
|
-
} else {
|
|
940
|
-
if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
scope == null ? void 0 : scope.activate(RedactionMode.Redact);
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
isRedactActive(documentId) {
|
|
947
|
-
var _a;
|
|
948
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
949
|
-
const activeMode = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode();
|
|
950
|
-
return activeMode === RedactionMode.Redact;
|
|
951
|
-
}
|
|
952
|
-
endRedact(documentId) {
|
|
953
|
-
var _a;
|
|
954
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
955
|
-
(_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activateDefaultMode();
|
|
956
|
-
}
|
|
957
|
-
// ─────────────────────────────────────────────────────────
|
|
958
|
-
// Public Methods
|
|
959
|
-
// ─────────────────────────────────────────────────────────
|
|
960
|
-
onRedactionSelectionChange(documentId, callback) {
|
|
961
|
-
const emitter = this.redactionSelection$.get(documentId);
|
|
962
|
-
return (emitter == null ? void 0 : emitter.on(callback)) ?? (() => {
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
onRedactionMarqueeChange(documentId, callback) {
|
|
966
|
-
const emitter = this.redactionMarquee$.get(documentId);
|
|
967
|
-
return (emitter == null ? void 0 : emitter.on(callback)) ?? (() => {
|
|
968
|
-
});
|
|
969
|
-
}
|
|
970
|
-
/**
|
|
971
|
-
* Get the stroke color for redaction previews.
|
|
972
|
-
* In annotation mode: returns tool's defaults.strokeColor
|
|
973
|
-
* In legacy mode: returns hardcoded red
|
|
974
|
-
*/
|
|
975
|
-
getPreviewStrokeColor() {
|
|
976
|
-
var _a;
|
|
977
|
-
if (this.useAnnotationMode) {
|
|
978
|
-
return ((_a = this.getRedactTool()) == null ? void 0 : _a.defaults.strokeColor) ?? "#FF0000";
|
|
979
|
-
}
|
|
980
|
-
return "#FF0000";
|
|
981
|
-
}
|
|
982
|
-
queueCurrentSelectionAsPending(documentId) {
|
|
983
|
-
const id = documentId ?? this.getActiveDocumentId();
|
|
984
|
-
if (!this.selectionCapability) {
|
|
985
|
-
return PdfTaskHelper.reject({
|
|
986
|
-
code: PdfErrorCode.NotFound,
|
|
987
|
-
message: "[RedactionPlugin] selection plugin required"
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
const coreDoc = this.coreState.core.documents[id];
|
|
991
|
-
if (!(coreDoc == null ? void 0 : coreDoc.document)) {
|
|
992
|
-
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
993
|
-
}
|
|
994
|
-
const selectionScope = this.selectionCapability.forDocument(id);
|
|
995
|
-
const formatted = selectionScope.getFormattedSelection();
|
|
996
|
-
if (!formatted.length) return PdfTaskHelper.resolve(true);
|
|
997
|
-
const textTask = selectionScope.getSelectedText();
|
|
998
|
-
const emitter = this.redactionSelection$.get(id);
|
|
999
|
-
emitter == null ? void 0 : emitter.emit([]);
|
|
1000
|
-
selectionScope.clear();
|
|
1001
|
-
if (!this.useAnnotationMode) {
|
|
1002
|
-
this.enableRedactSelection(id);
|
|
1003
|
-
}
|
|
1004
|
-
const task = new Task();
|
|
1005
|
-
textTask.wait(
|
|
1006
|
-
(textArr) => {
|
|
1007
|
-
const text = textArr.join(" ");
|
|
1008
|
-
this.createRedactionsFromSelection(id, formatted, text);
|
|
1009
|
-
task.resolve(true);
|
|
1010
|
-
},
|
|
1011
|
-
() => {
|
|
1012
|
-
this.createRedactionsFromSelection(id, formatted);
|
|
1013
|
-
task.resolve(true);
|
|
1014
|
-
}
|
|
1015
|
-
);
|
|
1016
|
-
return task;
|
|
1017
|
-
}
|
|
1018
|
-
commitPendingOne(page, id, documentId) {
|
|
1019
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1020
|
-
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyContents)) {
|
|
1021
|
-
this.logger.debug(
|
|
1022
|
-
"RedactionPlugin",
|
|
1023
|
-
"CommitPendingOne",
|
|
1024
|
-
`Cannot commit redaction: document ${docId} lacks ModifyContents permission`
|
|
1025
|
-
);
|
|
1026
|
-
return PdfTaskHelper.reject({
|
|
1027
|
-
code: PdfErrorCode.Security,
|
|
1028
|
-
message: "Document lacks ModifyContents permission"
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
1031
|
-
const coreDoc = this.coreState.core.documents[docId];
|
|
1032
|
-
if (!(coreDoc == null ? void 0 : coreDoc.document))
|
|
1033
|
-
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
1034
|
-
const pdfPage = coreDoc.document.pages[page];
|
|
1035
|
-
if (!pdfPage)
|
|
1036
|
-
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
|
|
1037
|
-
if (this.useAnnotationMode) {
|
|
1038
|
-
this.logger.debug(
|
|
1039
|
-
"RedactionPlugin",
|
|
1040
|
-
"CommitPendingOne",
|
|
1041
|
-
`Applying redaction in annotation mode: page ${page}, id ${id}`
|
|
1042
|
-
);
|
|
1043
|
-
return this.applyRedactionAnnotationMode(docId, coreDoc.document, pdfPage, id);
|
|
1044
|
-
}
|
|
1045
|
-
const docState = this.getDocumentState(docId);
|
|
1046
|
-
if (!docState) {
|
|
1047
|
-
return PdfTaskHelper.reject({
|
|
1048
|
-
code: PdfErrorCode.NotFound,
|
|
1049
|
-
message: "Document state not found"
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
const item = (docState.pending[page] ?? []).find((it) => it.id === id);
|
|
1053
|
-
if (!item) {
|
|
1054
|
-
this.logger.debug(
|
|
1055
|
-
"RedactionPlugin",
|
|
1056
|
-
"CommitPendingOne",
|
|
1057
|
-
`No pending item found for page ${page}, id ${id}`
|
|
1058
|
-
);
|
|
1059
|
-
return PdfTaskHelper.resolve(true);
|
|
1060
|
-
}
|
|
1061
|
-
return this.commitPendingOneLegacy(docId, coreDoc.document, pdfPage, page, item);
|
|
1062
|
-
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Legacy commit single redaction using redactTextInRects
|
|
1065
|
-
*/
|
|
1066
|
-
commitPendingOneLegacy(docId, doc, pdfPage, page, item) {
|
|
1067
|
-
const rects = item.kind === "text" ? item.rects : [item.rect];
|
|
1068
|
-
const task = new Task();
|
|
1069
|
-
this.engine.redactTextInRects(doc, pdfPage, rects, {
|
|
1070
|
-
drawBlackBoxes: this.config.drawBlackBoxes
|
|
1071
|
-
}).wait(
|
|
1072
|
-
() => {
|
|
1073
|
-
this.dispatch(removePending(docId, page, item.id));
|
|
1074
|
-
this.dispatchCoreAction(refreshPages(docId, [page]));
|
|
1075
|
-
this.events$.emit({ type: "commit", documentId: docId, success: true });
|
|
1076
|
-
task.resolve(true);
|
|
1077
|
-
},
|
|
1078
|
-
(error) => {
|
|
1079
|
-
this.events$.emit({
|
|
1080
|
-
type: "commit",
|
|
1081
|
-
documentId: docId,
|
|
1082
|
-
success: false,
|
|
1083
|
-
error: error.reason
|
|
1084
|
-
});
|
|
1085
|
-
task.reject({ code: PdfErrorCode.Unknown, message: "Failed to commit redactions" });
|
|
1086
|
-
}
|
|
1087
|
-
);
|
|
1088
|
-
return task;
|
|
1089
|
-
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Annotation mode: Apply single redaction using engine.applyRedaction
|
|
1092
|
-
*/
|
|
1093
|
-
applyRedactionAnnotationMode(docId, doc, pdfPage, annotationId) {
|
|
1094
|
-
var _a;
|
|
1095
|
-
const task = new Task();
|
|
1096
|
-
const anno = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(docId).getAnnotationById(annotationId);
|
|
1097
|
-
this.logger.debug(
|
|
1098
|
-
"RedactionPlugin",
|
|
1099
|
-
"ApplyRedactionAnnotationMode",
|
|
1100
|
-
`Looking for annotation ${annotationId}, found: ${!!anno}, type: ${anno == null ? void 0 : anno.object.type}`
|
|
1101
|
-
);
|
|
1102
|
-
if (!anno || anno.object.type !== PdfAnnotationSubtype.REDACT) {
|
|
1103
|
-
this.logger.warn(
|
|
1104
|
-
"RedactionPlugin",
|
|
1105
|
-
"ApplyRedactionAnnotationMode",
|
|
1106
|
-
`Redaction annotation not found or wrong type: ${annotationId}`
|
|
1107
|
-
);
|
|
1108
|
-
return PdfTaskHelper.reject({
|
|
1109
|
-
code: PdfErrorCode.NotFound,
|
|
1110
|
-
message: "Redaction annotation not found"
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
this.logger.debug(
|
|
1114
|
-
"RedactionPlugin",
|
|
1115
|
-
"ApplyRedactionAnnotationMode",
|
|
1116
|
-
`Calling engine.applyRedaction for annotation ${annotationId} on page ${pdfPage.index}`
|
|
1117
|
-
);
|
|
1118
|
-
this.engine.applyRedaction(doc, pdfPage, anno.object).wait(
|
|
1119
|
-
() => {
|
|
1120
|
-
var _a2, _b;
|
|
1121
|
-
this.logger.debug(
|
|
1122
|
-
"RedactionPlugin",
|
|
1123
|
-
"ApplyRedactionAnnotationMode",
|
|
1124
|
-
`Successfully applied redaction ${annotationId} on page ${pdfPage.index}`
|
|
1125
|
-
);
|
|
1126
|
-
(_a2 = this.annotationCapability) == null ? void 0 : _a2.forDocument(docId).purgeAnnotation(pdfPage.index, annotationId);
|
|
1127
|
-
this.dispatch(removePending(docId, pdfPage.index, annotationId));
|
|
1128
|
-
(_b = this.historyCapability) == null ? void 0 : _b.forDocument(docId).purgeByMetadata(
|
|
1129
|
-
(meta) => {
|
|
1130
|
-
var _a3;
|
|
1131
|
-
return ((_a3 = meta == null ? void 0 : meta.annotationIds) == null ? void 0 : _a3.includes(annotationId)) ?? false;
|
|
1132
|
-
},
|
|
1133
|
-
"annotations"
|
|
1134
|
-
);
|
|
1135
|
-
this.dispatchCoreAction(refreshPages(docId, [pdfPage.index]));
|
|
1136
|
-
this.events$.emit({ type: "commit", documentId: docId, success: true });
|
|
1137
|
-
task.resolve(true);
|
|
1138
|
-
},
|
|
1139
|
-
(error) => {
|
|
1140
|
-
var _a2;
|
|
1141
|
-
this.logger.error(
|
|
1142
|
-
"RedactionPlugin",
|
|
1143
|
-
"ApplyRedactionAnnotationMode",
|
|
1144
|
-
`Failed to apply redaction ${annotationId}: ${((_a2 = error.reason) == null ? void 0 : _a2.message) ?? "Unknown error"}`
|
|
1145
|
-
);
|
|
1146
|
-
this.events$.emit({
|
|
1147
|
-
type: "commit",
|
|
1148
|
-
documentId: docId,
|
|
1149
|
-
success: false,
|
|
1150
|
-
error: error.reason
|
|
1151
|
-
});
|
|
1152
|
-
task.reject({ code: PdfErrorCode.Unknown, message: "Failed to apply redaction" });
|
|
1153
|
-
}
|
|
1154
|
-
);
|
|
1155
|
-
return task;
|
|
1156
|
-
}
|
|
1157
|
-
commitAllPending(documentId) {
|
|
1158
|
-
const docId = documentId ?? this.getActiveDocumentId();
|
|
1159
|
-
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyContents)) {
|
|
1160
|
-
this.logger.debug(
|
|
1161
|
-
"RedactionPlugin",
|
|
1162
|
-
"CommitAllPending",
|
|
1163
|
-
`Cannot commit redactions: document ${docId} lacks ModifyContents permission`
|
|
1164
|
-
);
|
|
1165
|
-
return PdfTaskHelper.reject({
|
|
1166
|
-
code: PdfErrorCode.Security,
|
|
1167
|
-
message: "Document lacks ModifyContents permission"
|
|
1168
|
-
});
|
|
1169
|
-
}
|
|
1170
|
-
const coreDoc = this.coreState.core.documents[docId];
|
|
1171
|
-
if (!(coreDoc == null ? void 0 : coreDoc.document))
|
|
1172
|
-
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
1173
|
-
const docState = this.getDocumentState(docId);
|
|
1174
|
-
if (!docState) {
|
|
1175
|
-
return PdfTaskHelper.reject({
|
|
1176
|
-
code: PdfErrorCode.NotFound,
|
|
1177
|
-
message: "Document state not found"
|
|
1178
|
-
});
|
|
1179
|
-
}
|
|
1180
|
-
if (this.useAnnotationMode) {
|
|
1181
|
-
return this.applyAllRedactionsAnnotationMode(docId, coreDoc.document);
|
|
1182
|
-
} else {
|
|
1183
|
-
return this.commitAllPendingLegacy(docId, coreDoc.document, docState);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
/**
|
|
1187
|
-
* Legacy commit all redactions using redactTextInRects
|
|
1188
|
-
*/
|
|
1189
|
-
commitAllPendingLegacy(docId, doc, docState) {
|
|
1190
|
-
const perPage = /* @__PURE__ */ new Map();
|
|
1191
|
-
for (const [page, items] of Object.entries(docState.pending)) {
|
|
1192
|
-
const p = Number(page);
|
|
1193
|
-
const list = perPage.get(p) ?? [];
|
|
1194
|
-
for (const it of items) {
|
|
1195
|
-
if (it.kind === "text") list.push(...it.rects);
|
|
1196
|
-
else list.push(it.rect);
|
|
1197
|
-
}
|
|
1198
|
-
perPage.set(p, list);
|
|
1199
|
-
}
|
|
1200
|
-
const pagesToRefresh = Array.from(perPage.entries()).filter(([_, rects]) => rects.length > 0).map(([pageIndex]) => pageIndex);
|
|
1201
|
-
const tasks = [];
|
|
1202
|
-
for (const [pageIndex, rects] of perPage) {
|
|
1203
|
-
const page = doc.pages[pageIndex];
|
|
1204
|
-
if (!page) continue;
|
|
1205
|
-
if (!rects.length) continue;
|
|
1206
|
-
tasks.push(
|
|
1207
|
-
this.engine.redactTextInRects(doc, page, rects, {
|
|
1208
|
-
drawBlackBoxes: this.config.drawBlackBoxes
|
|
1209
|
-
})
|
|
1210
|
-
);
|
|
1211
|
-
}
|
|
1212
|
-
const task = new Task();
|
|
1213
|
-
Task.all(tasks).wait(
|
|
1214
|
-
() => {
|
|
1215
|
-
this.dispatch(clearPending(docId));
|
|
1216
|
-
this.dispatchCoreAction(refreshPages(docId, pagesToRefresh));
|
|
1217
|
-
this.events$.emit({ type: "commit", documentId: docId, success: true });
|
|
1218
|
-
task.resolve(true);
|
|
1219
|
-
},
|
|
1220
|
-
(error) => {
|
|
1221
|
-
this.events$.emit({
|
|
1222
|
-
type: "commit",
|
|
1223
|
-
documentId: docId,
|
|
1224
|
-
success: false,
|
|
1225
|
-
error: error.reason
|
|
1226
|
-
});
|
|
1227
|
-
task.reject({ code: PdfErrorCode.Unknown, message: "Failed to commit redactions" });
|
|
1228
|
-
}
|
|
1229
|
-
);
|
|
1230
|
-
return task;
|
|
1231
|
-
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Annotation mode: Apply all redactions using engine.applyAllRedactions per page
|
|
1234
|
-
*/
|
|
1235
|
-
applyAllRedactionsAnnotationMode(docId, doc) {
|
|
1236
|
-
const annotationCapability = this.annotationCapability;
|
|
1237
|
-
if (!annotationCapability) {
|
|
1238
|
-
return PdfTaskHelper.reject({
|
|
1239
|
-
code: PdfErrorCode.NotFound,
|
|
1240
|
-
message: "Annotation capability not found"
|
|
1241
|
-
});
|
|
1242
|
-
}
|
|
1243
|
-
const annoState = annotationCapability.forDocument(docId).getState();
|
|
1244
|
-
const redactAnnotationsByPage = /* @__PURE__ */ new Map();
|
|
1245
|
-
for (const ta of Object.values(annoState.byUid)) {
|
|
1246
|
-
if (ta.object.type === PdfAnnotationSubtype.REDACT) {
|
|
1247
|
-
const pageIds = redactAnnotationsByPage.get(ta.object.pageIndex) ?? [];
|
|
1248
|
-
pageIds.push(ta.object.id);
|
|
1249
|
-
redactAnnotationsByPage.set(ta.object.pageIndex, pageIds);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
const pagesToProcess = Array.from(redactAnnotationsByPage.keys());
|
|
1253
|
-
if (pagesToProcess.length === 0) {
|
|
1254
|
-
return PdfTaskHelper.resolve(true);
|
|
1255
|
-
}
|
|
1256
|
-
const tasks = [];
|
|
1257
|
-
for (const pageIndex of pagesToProcess) {
|
|
1258
|
-
const page = doc.pages[pageIndex];
|
|
1259
|
-
if (!page) continue;
|
|
1260
|
-
tasks.push(this.engine.applyAllRedactions(doc, page));
|
|
1261
|
-
}
|
|
1262
|
-
const task = new Task();
|
|
1263
|
-
Task.all(tasks).wait(
|
|
1264
|
-
() => {
|
|
1265
|
-
var _a;
|
|
1266
|
-
const annoScope = annotationCapability.forDocument(docId);
|
|
1267
|
-
const allPurgedIds = [];
|
|
1268
|
-
for (const [pageIndex, ids] of redactAnnotationsByPage) {
|
|
1269
|
-
for (const id of ids) {
|
|
1270
|
-
annoScope.purgeAnnotation(pageIndex, id);
|
|
1271
|
-
this.dispatch(removePending(docId, pageIndex, id));
|
|
1272
|
-
allPurgedIds.push(id);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
if (allPurgedIds.length > 0) {
|
|
1276
|
-
(_a = this.historyCapability) == null ? void 0 : _a.forDocument(docId).purgeByMetadata(
|
|
1277
|
-
(meta) => {
|
|
1278
|
-
var _a2;
|
|
1279
|
-
return ((_a2 = meta == null ? void 0 : meta.annotationIds) == null ? void 0 : _a2.some((id) => allPurgedIds.includes(id))) ?? false;
|
|
1280
|
-
},
|
|
1281
|
-
"annotations"
|
|
1282
|
-
);
|
|
1283
|
-
}
|
|
1284
|
-
this.dispatchCoreAction(refreshPages(docId, pagesToProcess));
|
|
1285
|
-
this.events$.emit({ type: "commit", documentId: docId, success: true });
|
|
1286
|
-
task.resolve(true);
|
|
1287
|
-
},
|
|
1288
|
-
(error) => {
|
|
1289
|
-
this.events$.emit({
|
|
1290
|
-
type: "commit",
|
|
1291
|
-
documentId: docId,
|
|
1292
|
-
success: false,
|
|
1293
|
-
error: error.reason
|
|
1294
|
-
});
|
|
1295
|
-
task.reject({ code: PdfErrorCode.Unknown, message: "Failed to apply redactions" });
|
|
1296
|
-
}
|
|
1297
|
-
);
|
|
1298
|
-
return task;
|
|
1299
|
-
}
|
|
1300
|
-
// ─────────────────────────────────────────────────────────
|
|
1301
|
-
// Annotation Mode Helpers
|
|
1302
|
-
// ─────────────────────────────────────────────────────────
|
|
1303
|
-
/**
|
|
1304
|
-
* Create REDACT annotations from text selection (annotation mode only)
|
|
1305
|
-
* @returns Array of annotation IDs that were created
|
|
1306
|
-
*/
|
|
1307
|
-
createRedactAnnotationsFromSelection(documentId, formattedSelection, text) {
|
|
1308
|
-
var _a;
|
|
1309
|
-
if (!this.annotationCapability) return [];
|
|
1310
|
-
const annoScope = this.annotationCapability.forDocument(documentId);
|
|
1311
|
-
const defaults = (_a = this.getRedactTool()) == null ? void 0 : _a.defaults;
|
|
1312
|
-
const annotationIds = [];
|
|
1313
|
-
for (const selection of formattedSelection) {
|
|
1314
|
-
const annotationId = uuidV4();
|
|
1315
|
-
annotationIds.push(annotationId);
|
|
1316
|
-
const annotation = {
|
|
1317
|
-
...defaults,
|
|
1318
|
-
id: annotationId,
|
|
1319
|
-
type: PdfAnnotationSubtype.REDACT,
|
|
1320
|
-
pageIndex: selection.pageIndex,
|
|
1321
|
-
rect: selection.rect,
|
|
1322
|
-
segmentRects: selection.segmentRects,
|
|
1323
|
-
...text ? { custom: { text } } : {},
|
|
1324
|
-
created: /* @__PURE__ */ new Date()
|
|
1325
|
-
};
|
|
1326
|
-
annoScope.createAnnotation(selection.pageIndex, annotation);
|
|
1327
|
-
if (selection === formattedSelection[formattedSelection.length - 1]) {
|
|
1328
|
-
annoScope.selectAnnotation(selection.pageIndex, annotationId);
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
if (text) {
|
|
1332
|
-
for (let i = 0; i < annotationIds.length; i++) {
|
|
1333
|
-
const pageIndex = formattedSelection[i].pageIndex;
|
|
1334
|
-
this.dispatch(updatePending(documentId, pageIndex, annotationIds[i], { text }));
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
return annotationIds;
|
|
1338
|
-
}
|
|
1339
|
-
/**
|
|
1340
|
-
* Create legacy RedactionItems from text selection (legacy mode only)
|
|
1341
|
-
*/
|
|
1342
|
-
createLegacyRedactionsFromSelection(documentId, formattedSelection, text) {
|
|
1343
|
-
const redactionColor = this.config.drawBlackBoxes ? "#000000" : "transparent";
|
|
1344
|
-
const items = formattedSelection.map((s) => ({
|
|
1345
|
-
id: uuidV4(),
|
|
1346
|
-
kind: "text",
|
|
1347
|
-
page: s.pageIndex,
|
|
1348
|
-
rect: s.rect,
|
|
1349
|
-
rects: s.segmentRects,
|
|
1350
|
-
source: "legacy",
|
|
1351
|
-
markColor: "#FF0000",
|
|
1352
|
-
redactionColor,
|
|
1353
|
-
text
|
|
1354
|
-
}));
|
|
1355
|
-
this.dispatch(addPending(documentId, items));
|
|
1356
|
-
if (items.length) {
|
|
1357
|
-
this.selectPending(items[items.length - 1].page, items[items.length - 1].id, documentId);
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
/**
|
|
1361
|
-
* Unified method to create redactions from text selection.
|
|
1362
|
-
* Delegates to annotation mode or legacy mode helper based on configuration.
|
|
1363
|
-
*/
|
|
1364
|
-
createRedactionsFromSelection(documentId, formattedSelection, text) {
|
|
1365
|
-
if (this.useAnnotationMode) {
|
|
1366
|
-
this.createRedactAnnotationsFromSelection(documentId, formattedSelection, text);
|
|
1367
|
-
} else {
|
|
1368
|
-
this.createLegacyRedactionsFromSelection(documentId, formattedSelection, text);
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
/**
|
|
1372
|
-
* Create a REDACT annotation from an area/marquee selection (annotation mode only)
|
|
1373
|
-
*/
|
|
1374
|
-
createRedactAnnotationFromArea(documentId, pageIndex, rect) {
|
|
1375
|
-
var _a;
|
|
1376
|
-
if (!this.annotationCapability) return;
|
|
1377
|
-
const annoScope = this.annotationCapability.forDocument(documentId);
|
|
1378
|
-
const defaults = (_a = this.getRedactTool()) == null ? void 0 : _a.defaults;
|
|
1379
|
-
const annotationId = uuidV4();
|
|
1380
|
-
const annotation = {
|
|
1381
|
-
...defaults,
|
|
1382
|
-
id: annotationId,
|
|
1383
|
-
type: PdfAnnotationSubtype.REDACT,
|
|
1384
|
-
pageIndex,
|
|
1385
|
-
rect,
|
|
1386
|
-
segmentRects: [],
|
|
1387
|
-
// No segment rects for area redaction
|
|
1388
|
-
created: /* @__PURE__ */ new Date()
|
|
1389
|
-
};
|
|
1390
|
-
annoScope.createAnnotation(pageIndex, annotation);
|
|
1391
|
-
annoScope.selectAnnotation(pageIndex, annotationId);
|
|
1392
|
-
}
|
|
1393
|
-
/**
|
|
1394
|
-
* Convert a RedactionItem to a PdfRedactAnnoObject
|
|
1395
|
-
*/
|
|
1396
|
-
redactionItemToAnnotation(item) {
|
|
1397
|
-
var _a;
|
|
1398
|
-
const defaults = ((_a = this.getRedactTool()) == null ? void 0 : _a.defaults) ?? {};
|
|
1399
|
-
return {
|
|
1400
|
-
...defaults,
|
|
1401
|
-
id: item.id,
|
|
1402
|
-
type: PdfAnnotationSubtype.REDACT,
|
|
1403
|
-
pageIndex: item.page,
|
|
1404
|
-
rect: item.rect,
|
|
1405
|
-
segmentRects: item.kind === "text" ? item.rects : [],
|
|
1406
|
-
created: /* @__PURE__ */ new Date()
|
|
1407
|
-
};
|
|
1408
|
-
}
|
|
1409
|
-
/**
|
|
1410
|
-
* Convert a PdfRedactAnnoObject to a RedactionItem
|
|
1411
|
-
*/
|
|
1412
|
-
annotationToRedactionItem(anno) {
|
|
1413
|
-
var _a;
|
|
1414
|
-
const markColor = anno.strokeColor ?? "#FF0000";
|
|
1415
|
-
const redactionColor = anno.color ?? "transparent";
|
|
1416
|
-
if (anno.segmentRects && anno.segmentRects.length > 0) {
|
|
1417
|
-
return {
|
|
1418
|
-
id: anno.id,
|
|
1419
|
-
kind: "text",
|
|
1420
|
-
page: anno.pageIndex,
|
|
1421
|
-
rect: anno.rect,
|
|
1422
|
-
rects: anno.segmentRects,
|
|
1423
|
-
source: "annotation",
|
|
1424
|
-
markColor,
|
|
1425
|
-
redactionColor,
|
|
1426
|
-
...((_a = anno.custom) == null ? void 0 : _a.text) ? { text: anno.custom.text } : {}
|
|
1427
|
-
};
|
|
1428
|
-
} else {
|
|
1429
|
-
return {
|
|
1430
|
-
id: anno.id,
|
|
1431
|
-
kind: "area",
|
|
1432
|
-
page: anno.pageIndex,
|
|
1433
|
-
rect: anno.rect,
|
|
1434
|
-
source: "annotation",
|
|
1435
|
-
markColor,
|
|
1436
|
-
redactionColor
|
|
1437
|
-
};
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
// ─────────────────────────────────────────────────────────
|
|
1441
|
-
// Event Emission Helpers
|
|
1442
|
-
// ─────────────────────────────────────────────────────────
|
|
1443
|
-
emitPendingChange(documentId) {
|
|
1444
|
-
const docState = this.getDocumentState(documentId);
|
|
1445
|
-
if (docState) {
|
|
1446
|
-
this.pending$.emit({ documentId, pending: docState.pending });
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
emitSelectedChange(documentId) {
|
|
1450
|
-
const docState = this.getDocumentState(documentId);
|
|
1451
|
-
if (docState) {
|
|
1452
|
-
this.selected$.emit({ documentId, selected: docState.selected });
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
emitStateChange(documentId) {
|
|
1456
|
-
const docState = this.getDocumentState(documentId);
|
|
1457
|
-
if (docState) {
|
|
1458
|
-
this.state$.emit({ documentId, state: docState });
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
// ─────────────────────────────────────────────────────────
|
|
1462
|
-
// Store Update Handlers
|
|
1463
|
-
// ─────────────────────────────────────────────────────────
|
|
1464
|
-
onStoreUpdated(_, newState) {
|
|
1465
|
-
for (const documentId in newState.documents) {
|
|
1466
|
-
const docState = newState.documents[documentId];
|
|
1467
|
-
if (docState) {
|
|
1468
|
-
this.emitPendingChange(documentId);
|
|
1469
|
-
this.emitSelectedChange(documentId);
|
|
1470
|
-
this.emitStateChange(documentId);
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
// ─────────────────────────────────────────────────────────
|
|
1475
|
-
// Lifecycle
|
|
1476
|
-
// ─────────────────────────────────────────────────────────
|
|
1477
|
-
async destroy() {
|
|
1478
|
-
this.pending$.clear();
|
|
1479
|
-
this.selected$.clear();
|
|
1480
|
-
this.state$.clear();
|
|
1481
|
-
this.events$.clear();
|
|
1482
|
-
this.redactionSelection$.forEach((emitter) => emitter.clear());
|
|
1483
|
-
this.redactionSelection$.clear();
|
|
1484
|
-
this.redactionMarquee$.forEach((emitter) => emitter.clear());
|
|
1485
|
-
this.redactionMarquee$.clear();
|
|
1486
|
-
this.documentUnsubscribers.forEach((unsubscribers) => {
|
|
1487
|
-
unsubscribers.forEach((unsub) => unsub());
|
|
1488
|
-
});
|
|
1489
|
-
this.documentUnsubscribers.clear();
|
|
1490
|
-
await super.destroy();
|
|
1491
|
-
}
|
|
1492
|
-
};
|
|
1493
|
-
_RedactionPlugin.id = "redaction";
|
|
1494
|
-
let RedactionPlugin = _RedactionPlugin;
|
|
1495
|
-
const REDACTION_PLUGIN_ID = "redaction";
|
|
1496
|
-
const manifest = {
|
|
1497
|
-
id: REDACTION_PLUGIN_ID,
|
|
1498
|
-
name: "Redaction Plugin",
|
|
1499
|
-
version: "1.0.0",
|
|
1500
|
-
provides: ["redaction"],
|
|
1501
|
-
requires: [],
|
|
1502
|
-
optional: ["interaction-manager", "selection", "annotation"],
|
|
1503
|
-
defaultConfig: {
|
|
1504
|
-
drawBlackBoxes: true
|
|
1505
|
-
}
|
|
1506
|
-
};
|
|
1507
|
-
const getPendingRedactionsCount = (s) => s.pendingCount;
|
|
1508
|
-
const hasPendingRedactions = (s) => s.pendingCount > 0;
|
|
1509
|
-
const getDocumentPendingCount = (state, documentId) => {
|
|
1510
|
-
var _a;
|
|
1511
|
-
return ((_a = state.documents[documentId]) == null ? void 0 : _a.pendingCount) ?? 0;
|
|
1512
|
-
};
|
|
1513
|
-
const getTotalPendingCount = (state) => {
|
|
1514
|
-
return Object.values(state.documents).reduce((sum, doc) => sum + doc.pendingCount, 0);
|
|
1515
|
-
};
|
|
1516
|
-
const RedactionPluginPackage = {
|
|
1517
|
-
manifest,
|
|
1518
|
-
create: (registry, config) => new RedactionPlugin(REDACTION_PLUGIN_ID, registry, config),
|
|
1519
|
-
reducer: redactionReducer,
|
|
1520
|
-
initialState
|
|
1521
|
-
};
|
|
1522
|
-
export {
|
|
1523
|
-
REDACTION_PLUGIN_ID,
|
|
1524
|
-
RedactionMode,
|
|
1525
|
-
RedactionPlugin,
|
|
1526
|
-
RedactionPluginPackage,
|
|
1527
|
-
getDocumentPendingCount,
|
|
1528
|
-
getPendingRedactionsCount,
|
|
1529
|
-
getTotalPendingCount,
|
|
1530
|
-
hasPendingRedactions,
|
|
1531
|
-
initialDocumentState,
|
|
1532
|
-
initialState,
|
|
1533
|
-
manifest,
|
|
1534
|
-
redactTool,
|
|
1535
|
-
redactTools
|
|
1536
|
-
};
|
|
1537
|
-
//# sourceMappingURL=index.js.map
|
|
222
|
+
//#endregion
|
|
223
|
+
export { RedactionToken, redactionPlugin };
|
|
224
|
+
|
|
225
|
+
//# sourceMappingURL=index.js.map
|