@embedpdf/plugin-annotation 1.0.5 → 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +509 -146
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +191 -29
- package/dist/index.d.ts +191 -29
- package/dist/index.js +512 -146
- package/dist/index.js.map +1 -1
- package/dist/preact/index.cjs +305 -111
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +303 -106
- package/dist/preact/index.js.map +1 -1
- package/package.json +10 -6
package/dist/index.js
CHANGED
|
@@ -5,89 +5,143 @@ var manifest = {
|
|
|
5
5
|
name: "Annotation Plugin",
|
|
6
6
|
version: "1.0.0",
|
|
7
7
|
provides: ["annotation"],
|
|
8
|
-
requires: [],
|
|
9
|
-
optional: [],
|
|
8
|
+
requires: ["interaction-manager", "selection"],
|
|
9
|
+
optional: ["history"],
|
|
10
10
|
defaultConfig: {
|
|
11
|
-
enabled: true
|
|
11
|
+
enabled: true,
|
|
12
|
+
autoCommit: true
|
|
12
13
|
}
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
// src/lib/annotation-plugin.ts
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["HIGHLIGHT"] = 9] = "HIGHLIGHT";
|
|
30
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["UNDERLINE"] = 10] = "UNDERLINE";
|
|
31
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SQUIGGLY"] = 11] = "SQUIGGLY";
|
|
32
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["STRIKEOUT"] = 12] = "STRIKEOUT";
|
|
33
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["STAMP"] = 13] = "STAMP";
|
|
34
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["CARET"] = 14] = "CARET";
|
|
35
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["INK"] = 15] = "INK";
|
|
36
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["POPUP"] = 16] = "POPUP";
|
|
37
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["FILEATTACHMENT"] = 17] = "FILEATTACHMENT";
|
|
38
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SOUND"] = 18] = "SOUND";
|
|
39
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["MOVIE"] = 19] = "MOVIE";
|
|
40
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["WIDGET"] = 20] = "WIDGET";
|
|
41
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SCREEN"] = 21] = "SCREEN";
|
|
42
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["PRINTERMARK"] = 22] = "PRINTERMARK";
|
|
43
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["TRAPNET"] = 23] = "TRAPNET";
|
|
44
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["WATERMARK"] = 24] = "WATERMARK";
|
|
45
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["THREED"] = 25] = "THREED";
|
|
46
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["RICHMEDIA"] = 26] = "RICHMEDIA";
|
|
47
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["XFAWIDGET"] = 27] = "XFAWIDGET";
|
|
48
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["REDACT"] = 28] = "REDACT";
|
|
49
|
-
return PdfAnnotationSubtype2;
|
|
50
|
-
})(PdfAnnotationSubtype || {});
|
|
51
|
-
function ignore() {
|
|
52
|
-
}
|
|
17
|
+
import {
|
|
18
|
+
BasePlugin,
|
|
19
|
+
createBehaviorEmitter,
|
|
20
|
+
enumEntries,
|
|
21
|
+
SET_DOCUMENT
|
|
22
|
+
} from "@embedpdf/core";
|
|
23
|
+
import {
|
|
24
|
+
ignore,
|
|
25
|
+
Task,
|
|
26
|
+
PdfAnnotationSubtype,
|
|
27
|
+
PdfTaskHelper,
|
|
28
|
+
PdfErrorCode
|
|
29
|
+
} from "@embedpdf/models";
|
|
53
30
|
|
|
54
31
|
// src/lib/actions.ts
|
|
55
|
-
var SET_ANNOTATIONS = "SET_ANNOTATIONS";
|
|
56
|
-
var
|
|
57
|
-
var
|
|
58
|
-
var
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
32
|
+
var SET_ANNOTATIONS = "ANNOTATION/SET_ANNOTATIONS";
|
|
33
|
+
var REINDEX_PAGE_ANNOTATIONS = "ANNOTATION/REINDEX_PAGE";
|
|
34
|
+
var SELECT_ANNOTATION = "ANNOTATION/SELECT_ANNOTATION";
|
|
35
|
+
var DESELECT_ANNOTATION = "ANNOTATION/DESELECT_ANNOTATION";
|
|
36
|
+
var SET_ANNOTATION_MODE = "ANNOTATION/SET_ANNOTATION_MODE";
|
|
37
|
+
var UPDATE_TOOL_DEFAULTS = "ANNOTATION/UPDATE_TOOL_DEFAULTS";
|
|
38
|
+
var ADD_COLOR_PRESET = "ANNOTATION/ADD_COLOR_PRESET";
|
|
39
|
+
var CREATE_ANNOTATION = "ANNOTATION/CREATE_ANNOTATION";
|
|
40
|
+
var PATCH_ANNOTATION = "ANNOTATION/PATCH_ANNOTATION";
|
|
41
|
+
var DELETE_ANNOTATION = "ANNOTATION/DELETE_ANNOTATION";
|
|
42
|
+
var COMMIT_PENDING_CHANGES = "ANNOTATION/COMMIT";
|
|
43
|
+
var STORE_PDF_ID = "ANNOTATION/STORE_PDF_ID";
|
|
44
|
+
var PURGE_ANNOTATION = "ANNOTATION/PURGE_ANNOTATION";
|
|
45
|
+
var setAnnotations = (p) => ({
|
|
46
|
+
type: SET_ANNOTATIONS,
|
|
47
|
+
payload: p
|
|
48
|
+
});
|
|
49
|
+
var reindexPageAnnotations = (pageIndex) => ({
|
|
50
|
+
type: REINDEX_PAGE_ANNOTATIONS,
|
|
51
|
+
payload: { pageIndex }
|
|
52
|
+
});
|
|
53
|
+
var selectAnnotation = (pageIndex, localId) => ({
|
|
54
|
+
type: SELECT_ANNOTATION,
|
|
55
|
+
payload: { pageIndex, localId }
|
|
56
|
+
});
|
|
57
|
+
var deselectAnnotation = () => ({ type: DESELECT_ANNOTATION });
|
|
58
|
+
var setAnnotationMode = (m) => ({
|
|
59
|
+
type: SET_ANNOTATION_MODE,
|
|
60
|
+
payload: m
|
|
61
|
+
});
|
|
62
|
+
var updateToolDefaults = (subtype, patch) => ({ type: UPDATE_TOOL_DEFAULTS, payload: { subtype, patch } });
|
|
63
|
+
var addColorPreset = (c) => ({
|
|
64
|
+
type: ADD_COLOR_PRESET,
|
|
65
|
+
payload: c
|
|
66
|
+
});
|
|
67
|
+
var createAnnotation = (pageIndex, localId, annotation) => ({
|
|
68
|
+
type: CREATE_ANNOTATION,
|
|
69
|
+
payload: { pageIndex, localId, annotation }
|
|
70
|
+
});
|
|
71
|
+
var patchAnnotation = (pageIndex, localId, patch) => ({
|
|
72
|
+
type: PATCH_ANNOTATION,
|
|
73
|
+
payload: { pageIndex, localId, patch }
|
|
74
|
+
});
|
|
75
|
+
var deleteAnnotation = (pageIndex, localId) => ({
|
|
76
|
+
type: DELETE_ANNOTATION,
|
|
77
|
+
payload: { pageIndex, localId }
|
|
78
|
+
});
|
|
79
|
+
var commitPendingChanges = () => ({ type: COMMIT_PENDING_CHANGES });
|
|
80
|
+
var storePdfId = (uid, pdfId) => ({
|
|
81
|
+
type: STORE_PDF_ID,
|
|
82
|
+
payload: { uid, pdfId }
|
|
83
|
+
});
|
|
84
|
+
var purgeAnnotation = (uid) => ({
|
|
85
|
+
type: PURGE_ANNOTATION,
|
|
86
|
+
payload: { uid }
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// src/lib/utils.ts
|
|
90
|
+
var makeUid = (pageIndex, localId) => `p${pageIndex}#${localId}`;
|
|
91
|
+
var parseUid = (uid) => {
|
|
92
|
+
const [pg, rest] = uid.slice(1).split("#");
|
|
93
|
+
return { pageIndex: Number(pg), localId: Number(rest) };
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// src/lib/selectors.ts
|
|
97
|
+
var makeUid2 = (page, id) => `p${page}#${id}`;
|
|
98
|
+
var getAnnotationsByPageIndex = (s, page) => (s.pages[page] ?? []).map((uid) => s.byUid[uid]);
|
|
99
|
+
var getAnnotations = (s) => {
|
|
100
|
+
const out = {};
|
|
101
|
+
for (const p of Object.keys(s.pages).map(Number)) out[p] = getAnnotationsByPageIndex(s, p);
|
|
102
|
+
return out;
|
|
103
|
+
};
|
|
104
|
+
var getSelectedAnnotation = (s) => s.selectedUid ? s.byUid[s.selectedUid] : null;
|
|
105
|
+
var getSelectedAnnotationWithPageIndex = (s) => {
|
|
106
|
+
if (!s.selectedUid) return null;
|
|
107
|
+
const { pageIndex, localId } = parseUid(s.selectedUid);
|
|
108
|
+
return { pageIndex, localId, annotation: s.byUid[s.selectedUid].object };
|
|
109
|
+
};
|
|
110
|
+
var getSelectedAnnotationByPageIndex = (s, pageIndex) => {
|
|
111
|
+
if (!s.selectedUid) return null;
|
|
112
|
+
const pageUids = s.pages[pageIndex] ?? [];
|
|
113
|
+
if (pageUids.includes(s.selectedUid)) {
|
|
114
|
+
return s.byUid[s.selectedUid];
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
var isInAnnotationMode = (s) => s.annotationMode !== null;
|
|
119
|
+
var getSelectedAnnotationMode = (s) => s.annotationMode;
|
|
120
|
+
var isAnnotationSelected = (s, page, id) => s.selectedUid === makeUid2(page, id);
|
|
84
121
|
|
|
85
122
|
// src/lib/annotation-plugin.ts
|
|
86
123
|
var AnnotationPlugin = class extends BasePlugin {
|
|
87
|
-
constructor(id, registry, engine) {
|
|
124
|
+
constructor(id, registry, engine, config) {
|
|
88
125
|
super(id, registry);
|
|
126
|
+
this.ANNOTATION_HISTORY_TOPIC = "annotations";
|
|
89
127
|
this.state$ = createBehaviorEmitter();
|
|
128
|
+
/** Map <subtype> → <modeId>. Filled once in `initialize()`. */
|
|
129
|
+
this.modeBySubtype = /* @__PURE__ */ new Map();
|
|
130
|
+
/** The inverse map for quick lookup in onModeChange(). */
|
|
131
|
+
this.subtypeByMode = /* @__PURE__ */ new Map();
|
|
132
|
+
this.modeChange$ = createBehaviorEmitter();
|
|
133
|
+
this.activeTool$ = createBehaviorEmitter({
|
|
134
|
+
mode: null,
|
|
135
|
+
defaults: null
|
|
136
|
+
});
|
|
90
137
|
this.engine = engine;
|
|
138
|
+
this.config = config;
|
|
139
|
+
const selection = registry.getPlugin("selection");
|
|
140
|
+
this.selection = selection?.provides() ?? null;
|
|
141
|
+
const history = registry.getPlugin("history");
|
|
142
|
+
this.history = history?.provides() ?? null;
|
|
143
|
+
const interactionManager = registry.getPlugin("interaction-manager");
|
|
144
|
+
this.interactionManager = interactionManager?.provides() ?? null;
|
|
91
145
|
this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {
|
|
92
146
|
const doc = state.core.document;
|
|
93
147
|
if (doc) {
|
|
@@ -96,29 +150,119 @@ var AnnotationPlugin = class extends BasePlugin {
|
|
|
96
150
|
});
|
|
97
151
|
}
|
|
98
152
|
async initialize() {
|
|
153
|
+
for (const [subtype, defaults] of enumEntries(this.state.toolDefaults)) {
|
|
154
|
+
this.registerTool(subtype, defaults);
|
|
155
|
+
}
|
|
156
|
+
this.history?.onHistoryChange((topic) => {
|
|
157
|
+
if (topic === this.ANNOTATION_HISTORY_TOPIC && this.config.autoCommit !== false) {
|
|
158
|
+
this.commit();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
this.interactionManager?.onModeChange((s) => {
|
|
162
|
+
const newSubtype = this.subtypeByMode.get(s.activeMode) ?? null;
|
|
163
|
+
if (newSubtype !== this.state.annotationMode) {
|
|
164
|
+
this.dispatch(setAnnotationMode(newSubtype));
|
|
165
|
+
this.modeChange$.emit(newSubtype);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
this.selection?.onEndSelection(() => {
|
|
169
|
+
if (!this.state.annotationMode) return;
|
|
170
|
+
const formattedSelection = this.selection?.getFormattedSelection();
|
|
171
|
+
if (!formattedSelection) return;
|
|
172
|
+
for (const selection of formattedSelection) {
|
|
173
|
+
const rect = selection.rect;
|
|
174
|
+
const segmentRects = selection.segmentRects;
|
|
175
|
+
const type = this.state.annotationMode;
|
|
176
|
+
const color = this.state.toolDefaults[type].color;
|
|
177
|
+
const opacity = this.state.toolDefaults[type].opacity;
|
|
178
|
+
this.createAnnotation(selection.pageIndex, {
|
|
179
|
+
type,
|
|
180
|
+
rect,
|
|
181
|
+
segmentRects,
|
|
182
|
+
color,
|
|
183
|
+
opacity,
|
|
184
|
+
pageIndex: selection.pageIndex,
|
|
185
|
+
id: Date.now() + Math.random()
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
this.selection?.clear();
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
registerTool(subtype, defaults) {
|
|
192
|
+
const modeId = defaults.interaction.mode;
|
|
193
|
+
const interactionMode = {
|
|
194
|
+
id: modeId,
|
|
195
|
+
scope: "page",
|
|
196
|
+
exclusive: defaults.interaction.exclusive,
|
|
197
|
+
cursor: defaults.interaction.cursor
|
|
198
|
+
};
|
|
199
|
+
this.interactionManager?.registerMode(interactionMode);
|
|
200
|
+
if (defaults.textSelection) {
|
|
201
|
+
this.selection?.enableForMode(modeId);
|
|
202
|
+
}
|
|
203
|
+
this.modeBySubtype.set(subtype, modeId);
|
|
204
|
+
this.subtypeByMode.set(modeId, subtype);
|
|
99
205
|
}
|
|
100
206
|
buildCapability() {
|
|
101
207
|
return {
|
|
102
208
|
getPageAnnotations: (options) => {
|
|
103
209
|
return this.getPageAnnotations(options);
|
|
104
210
|
},
|
|
211
|
+
getSelectedAnnotation: () => {
|
|
212
|
+
return getSelectedAnnotation(this.state);
|
|
213
|
+
},
|
|
105
214
|
selectAnnotation: (pageIndex, annotationId) => {
|
|
106
215
|
this.selectAnnotation(pageIndex, annotationId);
|
|
107
216
|
},
|
|
108
217
|
deselectAnnotation: () => {
|
|
109
218
|
this.dispatch(deselectAnnotation());
|
|
110
219
|
},
|
|
111
|
-
|
|
112
|
-
return this.
|
|
220
|
+
getAnnotationMode: () => {
|
|
221
|
+
return this.state.annotationMode;
|
|
222
|
+
},
|
|
223
|
+
setAnnotationMode: (subtype) => {
|
|
224
|
+
if (subtype === this.state.annotationMode) return;
|
|
225
|
+
if (subtype) {
|
|
226
|
+
const mode = this.modeBySubtype.get(subtype);
|
|
227
|
+
if (!mode) throw new Error(`Mode missing for subtype ${subtype}`);
|
|
228
|
+
this.interactionManager?.activate(mode);
|
|
229
|
+
} else {
|
|
230
|
+
this.interactionManager?.activate("default");
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
getToolDefaults: (subtype) => {
|
|
234
|
+
const defaults = this.state.toolDefaults[subtype];
|
|
235
|
+
if (!defaults) {
|
|
236
|
+
throw new Error(`No defaults found for subtype: ${subtype}`);
|
|
237
|
+
}
|
|
238
|
+
return defaults;
|
|
113
239
|
},
|
|
114
|
-
|
|
115
|
-
this.dispatch(
|
|
240
|
+
setToolDefaults: (subtype, patch) => {
|
|
241
|
+
this.dispatch(updateToolDefaults(subtype, patch));
|
|
116
242
|
},
|
|
117
|
-
|
|
243
|
+
getColorPresets: () => [...this.state.colorPresets],
|
|
244
|
+
addColorPreset: (color) => this.dispatch(addColorPreset(color)),
|
|
245
|
+
createAnnotation: (pageIndex, annotation) => this.createAnnotation(pageIndex, annotation),
|
|
246
|
+
updateAnnotation: (pageIndex, localId, patch) => this.updateAnnotation(pageIndex, localId, patch),
|
|
247
|
+
deleteAnnotation: (pageIndex, localId) => this.deleteAnnotation(pageIndex, localId),
|
|
248
|
+
onStateChange: this.state$.on,
|
|
249
|
+
onModeChange: this.modeChange$.on,
|
|
250
|
+
onActiveToolChange: this.activeTool$.on,
|
|
251
|
+
commit: () => this.commit()
|
|
118
252
|
};
|
|
119
253
|
}
|
|
120
|
-
|
|
121
|
-
|
|
254
|
+
emitActiveTool(state) {
|
|
255
|
+
const mode = state.annotationMode;
|
|
256
|
+
this.activeTool$.emit({
|
|
257
|
+
mode,
|
|
258
|
+
defaults: mode ? state.toolDefaults[mode] : null
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
onStoreUpdated(prev, next) {
|
|
262
|
+
this.state$.emit(next);
|
|
263
|
+
if (prev.annotationMode !== next.annotationMode || prev.toolDefaults[prev.annotationMode ?? PdfAnnotationSubtype.HIGHLIGHT] !== next.toolDefaults[next.annotationMode ?? PdfAnnotationSubtype.HIGHLIGHT]) {
|
|
264
|
+
this.emitActiveTool(next);
|
|
265
|
+
}
|
|
122
266
|
}
|
|
123
267
|
getAllAnnotations(doc) {
|
|
124
268
|
const task = this.engine.getAllAnnotations(doc);
|
|
@@ -128,114 +272,328 @@ var AnnotationPlugin = class extends BasePlugin {
|
|
|
128
272
|
const { pageIndex } = options;
|
|
129
273
|
const doc = this.coreState.core.document;
|
|
130
274
|
if (!doc) {
|
|
131
|
-
|
|
275
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
132
276
|
}
|
|
133
277
|
const page = doc.pages.find((p) => p.index === pageIndex);
|
|
134
278
|
if (!page) {
|
|
135
|
-
|
|
279
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
|
|
136
280
|
}
|
|
137
281
|
return this.engine.getPageAnnotations(doc, page);
|
|
138
282
|
}
|
|
139
283
|
selectAnnotation(pageIndex, annotationId) {
|
|
140
|
-
|
|
141
|
-
|
|
284
|
+
this.dispatch(selectAnnotation(pageIndex, annotationId));
|
|
285
|
+
}
|
|
286
|
+
createAnnotation(pageIndex, annotation) {
|
|
287
|
+
const localId = annotation.id;
|
|
288
|
+
const execute = () => this.dispatch(createAnnotation(pageIndex, localId, annotation));
|
|
289
|
+
if (!this.history) {
|
|
290
|
+
execute();
|
|
291
|
+
if (this.config.autoCommit) this.commit();
|
|
142
292
|
return;
|
|
143
293
|
}
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
294
|
+
const command = {
|
|
295
|
+
execute,
|
|
296
|
+
undo: () => {
|
|
297
|
+
this.dispatch(deselectAnnotation());
|
|
298
|
+
this.dispatch(deleteAnnotation(pageIndex, localId));
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
this.history.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
148
302
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
303
|
+
updateAnnotation(pageIndex, localId, patch) {
|
|
304
|
+
if (!this.history) {
|
|
305
|
+
this.dispatch(patchAnnotation(pageIndex, localId, patch));
|
|
306
|
+
if (this.config.autoCommit !== false) {
|
|
307
|
+
this.commit();
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
153
310
|
}
|
|
154
|
-
|
|
155
|
-
|
|
311
|
+
const originalObject = this.state.byUid[makeUid(pageIndex, localId)].object;
|
|
312
|
+
const originalPatch = Object.fromEntries(
|
|
313
|
+
Object.keys(patch).map((key) => [key, originalObject[key]])
|
|
314
|
+
);
|
|
315
|
+
const command = {
|
|
316
|
+
execute: () => this.dispatch(patchAnnotation(pageIndex, localId, patch)),
|
|
317
|
+
undo: () => this.dispatch(patchAnnotation(pageIndex, localId, originalPatch))
|
|
318
|
+
};
|
|
319
|
+
this.history.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
320
|
+
}
|
|
321
|
+
deleteAnnotation(pageIndex, localId) {
|
|
322
|
+
if (!this.history) {
|
|
323
|
+
this.dispatch(deselectAnnotation());
|
|
324
|
+
this.dispatch(deleteAnnotation(pageIndex, localId));
|
|
325
|
+
if (this.config.autoCommit !== false) {
|
|
326
|
+
this.commit();
|
|
327
|
+
}
|
|
328
|
+
return;
|
|
156
329
|
}
|
|
330
|
+
const originalAnnotation = this.state.byUid[makeUid(pageIndex, localId)].object;
|
|
331
|
+
const command = {
|
|
332
|
+
execute: () => {
|
|
333
|
+
this.dispatch(deselectAnnotation());
|
|
334
|
+
this.dispatch(deleteAnnotation(pageIndex, localId));
|
|
335
|
+
},
|
|
336
|
+
undo: () => this.dispatch(createAnnotation(pageIndex, localId, originalAnnotation))
|
|
337
|
+
};
|
|
338
|
+
this.history.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
339
|
+
}
|
|
340
|
+
commit() {
|
|
341
|
+
const task = new Task();
|
|
342
|
+
if (!this.state.hasPendingChanges) return PdfTaskHelper.resolve(true);
|
|
157
343
|
const doc = this.coreState.core.document;
|
|
158
|
-
if (!doc)
|
|
159
|
-
return
|
|
344
|
+
if (!doc)
|
|
345
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
346
|
+
const creations = [];
|
|
347
|
+
const updates = [];
|
|
348
|
+
const deletionsByPage = /* @__PURE__ */ new Map();
|
|
349
|
+
const affectedPages = /* @__PURE__ */ new Set();
|
|
350
|
+
for (const [uid, ta] of Object.entries(this.state.byUid)) {
|
|
351
|
+
if (ta.commitState === "synced") continue;
|
|
352
|
+
const { pageIndex } = parseUid(uid);
|
|
353
|
+
const page = doc.pages.find((p) => p.index === pageIndex);
|
|
354
|
+
if (!page) continue;
|
|
355
|
+
affectedPages.add(pageIndex);
|
|
356
|
+
switch (ta.commitState) {
|
|
357
|
+
case "new":
|
|
358
|
+
const task2 = this.engine.createPageAnnotation(doc, page, ta.object);
|
|
359
|
+
task2.wait((annoId) => this.dispatch(storePdfId(uid, annoId)), ignore);
|
|
360
|
+
creations.push(task2);
|
|
361
|
+
break;
|
|
362
|
+
case "dirty":
|
|
363
|
+
updates.push(
|
|
364
|
+
this.engine.updatePageAnnotation(doc, page, { ...ta.object, id: ta.pdfId })
|
|
365
|
+
);
|
|
366
|
+
break;
|
|
367
|
+
case "deleted":
|
|
368
|
+
if (!deletionsByPage.has(pageIndex)) {
|
|
369
|
+
deletionsByPage.set(pageIndex, []);
|
|
370
|
+
}
|
|
371
|
+
deletionsByPage.get(pageIndex).push({ ta, uid });
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
160
374
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
375
|
+
const deletionTasks = [];
|
|
376
|
+
for (const [pageIndex, deletions] of deletionsByPage.entries()) {
|
|
377
|
+
const page = doc.pages.find((p) => p.index === pageIndex);
|
|
378
|
+
deletions.sort((a, b) => (b.ta.pdfId ?? -1) - (a.ta.pdfId ?? -1));
|
|
379
|
+
for (const { ta, uid } of deletions) {
|
|
380
|
+
if (ta.pdfId !== void 0) {
|
|
381
|
+
const task2 = new Task();
|
|
382
|
+
const removeTask = this.engine.removePageAnnotation(doc, page, {
|
|
383
|
+
...ta.object,
|
|
384
|
+
id: ta.pdfId
|
|
385
|
+
});
|
|
386
|
+
removeTask.wait(() => {
|
|
387
|
+
this.dispatch(purgeAnnotation(uid));
|
|
388
|
+
task2.resolve(true);
|
|
389
|
+
}, task2.fail);
|
|
390
|
+
deletionTasks.push(task2);
|
|
391
|
+
} else {
|
|
392
|
+
this.dispatch(purgeAnnotation(uid));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
167
395
|
}
|
|
396
|
+
const allWriteTasks = [...creations, ...updates, ...deletionTasks];
|
|
397
|
+
Task.allSettled(allWriteTasks).wait(() => {
|
|
398
|
+
for (const pageIndex of affectedPages) {
|
|
399
|
+
this.dispatch(reindexPageAnnotations(pageIndex));
|
|
400
|
+
}
|
|
401
|
+
this.dispatch(commitPendingChanges());
|
|
402
|
+
task.resolve(true);
|
|
403
|
+
}, task.fail);
|
|
404
|
+
return task;
|
|
168
405
|
}
|
|
169
406
|
};
|
|
170
407
|
AnnotationPlugin.id = "annotation";
|
|
171
408
|
|
|
172
409
|
// src/lib/reducer.ts
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
410
|
+
import { PdfAnnotationSubtype as PdfAnnotationSubtype2 } from "@embedpdf/models";
|
|
411
|
+
var DEFAULT_COLORS = [
|
|
412
|
+
"#E44234",
|
|
413
|
+
"#FF8D00",
|
|
414
|
+
"#FFCD45",
|
|
415
|
+
"#5CC96E",
|
|
416
|
+
"#25D2D1",
|
|
417
|
+
"#597CE2",
|
|
418
|
+
"#C544CE",
|
|
419
|
+
"#7D2E25"
|
|
420
|
+
];
|
|
421
|
+
var patchAnno = (state, uid, patch) => {
|
|
422
|
+
const prev = state.byUid[uid];
|
|
423
|
+
if (!prev) return state;
|
|
424
|
+
return {
|
|
425
|
+
...state,
|
|
426
|
+
byUid: {
|
|
427
|
+
...state.byUid,
|
|
428
|
+
[uid]: {
|
|
429
|
+
...prev,
|
|
430
|
+
commitState: prev.commitState === "synced" ? "dirty" : prev.commitState,
|
|
431
|
+
object: { ...prev.object, ...patch }
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
hasPendingChanges: true
|
|
435
|
+
};
|
|
177
436
|
};
|
|
437
|
+
var initialState = (cfg) => ({
|
|
438
|
+
pages: {},
|
|
439
|
+
byUid: {},
|
|
440
|
+
selectedUid: null,
|
|
441
|
+
annotationMode: null,
|
|
442
|
+
toolDefaults: {
|
|
443
|
+
[PdfAnnotationSubtype2.HIGHLIGHT]: {
|
|
444
|
+
name: "Highlight",
|
|
445
|
+
color: "#FFCD45",
|
|
446
|
+
opacity: 1,
|
|
447
|
+
interaction: { mode: "highlight", exclusive: false },
|
|
448
|
+
textSelection: true
|
|
449
|
+
},
|
|
450
|
+
[PdfAnnotationSubtype2.UNDERLINE]: {
|
|
451
|
+
name: "Underline",
|
|
452
|
+
color: "#E44234",
|
|
453
|
+
opacity: 1,
|
|
454
|
+
interaction: { mode: "underline", exclusive: false },
|
|
455
|
+
textSelection: true
|
|
456
|
+
},
|
|
457
|
+
[PdfAnnotationSubtype2.STRIKEOUT]: {
|
|
458
|
+
name: "Strikeout",
|
|
459
|
+
color: "#E44234",
|
|
460
|
+
opacity: 1,
|
|
461
|
+
interaction: { mode: "strikeout", exclusive: false },
|
|
462
|
+
textSelection: true
|
|
463
|
+
},
|
|
464
|
+
[PdfAnnotationSubtype2.SQUIGGLY]: {
|
|
465
|
+
name: "Squiggly",
|
|
466
|
+
color: "#E44234",
|
|
467
|
+
opacity: 1,
|
|
468
|
+
interaction: { mode: "squiggly", exclusive: false },
|
|
469
|
+
textSelection: true
|
|
470
|
+
},
|
|
471
|
+
...cfg.toolDefaults
|
|
472
|
+
},
|
|
473
|
+
colorPresets: cfg.colorPresets ?? DEFAULT_COLORS,
|
|
474
|
+
hasPendingChanges: false
|
|
475
|
+
});
|
|
178
476
|
var reducer = (state, action) => {
|
|
179
477
|
switch (action.type) {
|
|
180
|
-
|
|
478
|
+
/* ───── bulk load from engine ───── */
|
|
479
|
+
case SET_ANNOTATIONS: {
|
|
480
|
+
const newPages = { ...state.pages };
|
|
481
|
+
const newByUid = { ...state.byUid };
|
|
482
|
+
for (const [pgStr, list] of Object.entries(action.payload)) {
|
|
483
|
+
const pageIndex = Number(pgStr);
|
|
484
|
+
const oldUidsOnPage = state.pages[pageIndex] || [];
|
|
485
|
+
for (const uid of oldUidsOnPage) {
|
|
486
|
+
delete newByUid[uid];
|
|
487
|
+
}
|
|
488
|
+
const newUidsOnPage = list.map((a, index) => {
|
|
489
|
+
const localId = Date.now() + Math.random() + index;
|
|
490
|
+
const uid = makeUid(pageIndex, localId);
|
|
491
|
+
newByUid[uid] = { localId, pdfId: a.id, commitState: "synced", object: a };
|
|
492
|
+
return uid;
|
|
493
|
+
});
|
|
494
|
+
newPages[pageIndex] = newUidsOnPage;
|
|
495
|
+
}
|
|
496
|
+
return { ...state, pages: newPages, byUid: newByUid };
|
|
497
|
+
}
|
|
498
|
+
/* ───── GUI bits ───── */
|
|
499
|
+
case SET_ANNOTATION_MODE:
|
|
500
|
+
return { ...state, annotationMode: action.payload };
|
|
501
|
+
case SELECT_ANNOTATION:
|
|
181
502
|
return {
|
|
182
503
|
...state,
|
|
183
|
-
|
|
184
|
-
// Clear selection if the annotations have changed
|
|
185
|
-
selectedAnnotation: null
|
|
504
|
+
selectedUid: makeUid(action.payload.pageIndex, action.payload.localId)
|
|
186
505
|
};
|
|
187
|
-
case
|
|
506
|
+
case DESELECT_ANNOTATION:
|
|
507
|
+
return { ...state, selectedUid: null };
|
|
508
|
+
case ADD_COLOR_PRESET:
|
|
509
|
+
return state.colorPresets.includes(action.payload) ? state : { ...state, colorPresets: [...state.colorPresets, action.payload] };
|
|
510
|
+
case UPDATE_TOOL_DEFAULTS: {
|
|
511
|
+
const { subtype, patch } = action.payload;
|
|
188
512
|
return {
|
|
189
513
|
...state,
|
|
190
|
-
|
|
514
|
+
toolDefaults: {
|
|
515
|
+
...state.toolDefaults,
|
|
516
|
+
[subtype]: { ...state.toolDefaults[subtype], ...patch }
|
|
517
|
+
}
|
|
191
518
|
};
|
|
192
|
-
|
|
519
|
+
}
|
|
520
|
+
/* ───── create ───── */
|
|
521
|
+
case CREATE_ANNOTATION: {
|
|
522
|
+
const { pageIndex, localId, annotation } = action.payload;
|
|
523
|
+
const uid = makeUid(pageIndex, localId);
|
|
193
524
|
return {
|
|
194
525
|
...state,
|
|
195
|
-
|
|
526
|
+
pages: { ...state.pages, [pageIndex]: [...state.pages[pageIndex] ?? [], uid] },
|
|
527
|
+
byUid: {
|
|
528
|
+
...state.byUid,
|
|
529
|
+
[uid]: { localId, pdfId: void 0, commitState: "new", object: annotation }
|
|
530
|
+
},
|
|
531
|
+
hasPendingChanges: true
|
|
196
532
|
};
|
|
197
|
-
|
|
533
|
+
}
|
|
534
|
+
/* ───── delete ───── */
|
|
535
|
+
case DELETE_ANNOTATION: {
|
|
536
|
+
const { pageIndex, localId } = action.payload;
|
|
537
|
+
const uid = makeUid(pageIndex, localId);
|
|
538
|
+
if (!state.byUid[uid]) return state;
|
|
198
539
|
return {
|
|
199
540
|
...state,
|
|
200
|
-
|
|
541
|
+
pages: {
|
|
542
|
+
...state.pages,
|
|
543
|
+
[pageIndex]: (state.pages[pageIndex] ?? []).filter((u) => u !== uid)
|
|
544
|
+
},
|
|
545
|
+
byUid: {
|
|
546
|
+
...state.byUid,
|
|
547
|
+
[uid]: { ...state.byUid[uid], commitState: "deleted" }
|
|
548
|
+
},
|
|
549
|
+
hasPendingChanges: true
|
|
201
550
|
};
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
551
|
+
}
|
|
552
|
+
/* ───── field edits ───── */
|
|
553
|
+
case PATCH_ANNOTATION: {
|
|
554
|
+
const uid = makeUid(action.payload.pageIndex, action.payload.localId);
|
|
555
|
+
return patchAnno(state, uid, action.payload.patch);
|
|
556
|
+
}
|
|
557
|
+
/* ───── commit bookkeeping ───── */
|
|
558
|
+
case COMMIT_PENDING_CHANGES: {
|
|
559
|
+
const cleaned = {};
|
|
560
|
+
for (const [uid, ta] of Object.entries(state.byUid)) {
|
|
561
|
+
cleaned[uid] = {
|
|
562
|
+
...ta,
|
|
563
|
+
commitState: ta.commitState === "dirty" || ta.commitState === "new" ? "synced" : ta.commitState
|
|
564
|
+
};
|
|
207
565
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
566
|
+
return { ...state, byUid: cleaned, hasPendingChanges: false };
|
|
567
|
+
}
|
|
568
|
+
case REINDEX_PAGE_ANNOTATIONS: {
|
|
569
|
+
const { pageIndex } = action.payload;
|
|
570
|
+
const newByUid = { ...state.byUid };
|
|
571
|
+
const uidsOnPage = state.pages[pageIndex] || [];
|
|
572
|
+
const annosOnPage = uidsOnPage.map((uid) => state.byUid[uid]).filter((ta) => ta && ta.commitState !== "deleted");
|
|
573
|
+
annosOnPage.sort((a, b) => (a.pdfId ?? Infinity) - (b.pdfId ?? Infinity));
|
|
574
|
+
annosOnPage.forEach((ta, newPdfId) => {
|
|
575
|
+
const uid = makeUid(pageIndex, ta.localId);
|
|
576
|
+
newByUid[uid] = { ...newByUid[uid], pdfId: newPdfId };
|
|
218
577
|
});
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
if (
|
|
225
|
-
const updatedAnnotation = updatedAnnotations.find((a) => a.id === annotationId);
|
|
226
|
-
if (updatedAnnotation) {
|
|
227
|
-
newSelectedAnnotation = {
|
|
228
|
-
...newSelectedAnnotation,
|
|
229
|
-
annotation: updatedAnnotation
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
}
|
|
578
|
+
return { ...state, byUid: newByUid };
|
|
579
|
+
}
|
|
580
|
+
case STORE_PDF_ID: {
|
|
581
|
+
const { uid, pdfId } = action.payload;
|
|
582
|
+
const ta = state.byUid[uid];
|
|
583
|
+
if (!ta) return state;
|
|
233
584
|
return {
|
|
234
585
|
...state,
|
|
235
|
-
|
|
236
|
-
|
|
586
|
+
byUid: {
|
|
587
|
+
...state.byUid,
|
|
588
|
+
[uid]: { ...ta, pdfId, commitState: "synced" }
|
|
589
|
+
}
|
|
237
590
|
};
|
|
238
591
|
}
|
|
592
|
+
case PURGE_ANNOTATION: {
|
|
593
|
+
const { uid } = action.payload;
|
|
594
|
+
const { [uid]: _gone, ...rest } = state.byUid;
|
|
595
|
+
return { ...state, byUid: rest };
|
|
596
|
+
}
|
|
239
597
|
default:
|
|
240
598
|
return state;
|
|
241
599
|
}
|
|
@@ -244,14 +602,22 @@ var reducer = (state, action) => {
|
|
|
244
602
|
// src/lib/index.ts
|
|
245
603
|
var AnnotationPluginPackage = {
|
|
246
604
|
manifest,
|
|
247
|
-
create: (registry, engine) => new AnnotationPlugin(ANNOTATION_PLUGIN_ID, registry, engine),
|
|
605
|
+
create: (registry, engine, config) => new AnnotationPlugin(ANNOTATION_PLUGIN_ID, registry, engine, config),
|
|
248
606
|
reducer,
|
|
249
|
-
initialState
|
|
607
|
+
initialState: (_, config) => initialState(config)
|
|
250
608
|
};
|
|
251
609
|
export {
|
|
252
610
|
ANNOTATION_PLUGIN_ID,
|
|
253
611
|
AnnotationPlugin,
|
|
254
612
|
AnnotationPluginPackage,
|
|
613
|
+
getAnnotations,
|
|
614
|
+
getAnnotationsByPageIndex,
|
|
615
|
+
getSelectedAnnotation,
|
|
616
|
+
getSelectedAnnotationByPageIndex,
|
|
617
|
+
getSelectedAnnotationMode,
|
|
618
|
+
getSelectedAnnotationWithPageIndex,
|
|
619
|
+
isAnnotationSelected,
|
|
620
|
+
isInAnnotationMode,
|
|
255
621
|
manifest
|
|
256
622
|
};
|
|
257
623
|
//# sourceMappingURL=index.js.map
|