@embedpdf/plugin-annotation 1.0.6 → 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 -161
- 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 -161
- package/dist/index.js.map +1 -1
- package/dist/preact/index.cjs +305 -126
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +303 -121
- package/dist/preact/index.js.map +1 -1
- package/package.json +10 -6
package/dist/index.js
CHANGED
|
@@ -5,104 +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
|
-
PdfBomOrZwnbsp,
|
|
30
|
-
PdfNonCharacterFFFE,
|
|
31
|
-
PdfNonCharacterFFFF
|
|
32
|
-
]);
|
|
33
|
-
var PdfUnwantedTextRegex = new RegExp(`[${PdfUnwantedTextMarkers.join("")}]`, "g");
|
|
34
|
-
var PdfAnnotationSubtype = /* @__PURE__ */ ((PdfAnnotationSubtype2) => {
|
|
35
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["UNKNOWN"] = 0] = "UNKNOWN";
|
|
36
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["TEXT"] = 1] = "TEXT";
|
|
37
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["LINK"] = 2] = "LINK";
|
|
38
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["FREETEXT"] = 3] = "FREETEXT";
|
|
39
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["LINE"] = 4] = "LINE";
|
|
40
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SQUARE"] = 5] = "SQUARE";
|
|
41
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["CIRCLE"] = 6] = "CIRCLE";
|
|
42
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["POLYGON"] = 7] = "POLYGON";
|
|
43
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["POLYLINE"] = 8] = "POLYLINE";
|
|
44
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["HIGHLIGHT"] = 9] = "HIGHLIGHT";
|
|
45
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["UNDERLINE"] = 10] = "UNDERLINE";
|
|
46
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SQUIGGLY"] = 11] = "SQUIGGLY";
|
|
47
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["STRIKEOUT"] = 12] = "STRIKEOUT";
|
|
48
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["STAMP"] = 13] = "STAMP";
|
|
49
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["CARET"] = 14] = "CARET";
|
|
50
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["INK"] = 15] = "INK";
|
|
51
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["POPUP"] = 16] = "POPUP";
|
|
52
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["FILEATTACHMENT"] = 17] = "FILEATTACHMENT";
|
|
53
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SOUND"] = 18] = "SOUND";
|
|
54
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["MOVIE"] = 19] = "MOVIE";
|
|
55
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["WIDGET"] = 20] = "WIDGET";
|
|
56
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["SCREEN"] = 21] = "SCREEN";
|
|
57
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["PRINTERMARK"] = 22] = "PRINTERMARK";
|
|
58
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["TRAPNET"] = 23] = "TRAPNET";
|
|
59
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["WATERMARK"] = 24] = "WATERMARK";
|
|
60
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["THREED"] = 25] = "THREED";
|
|
61
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["RICHMEDIA"] = 26] = "RICHMEDIA";
|
|
62
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["XFAWIDGET"] = 27] = "XFAWIDGET";
|
|
63
|
-
PdfAnnotationSubtype2[PdfAnnotationSubtype2["REDACT"] = 28] = "REDACT";
|
|
64
|
-
return PdfAnnotationSubtype2;
|
|
65
|
-
})(PdfAnnotationSubtype || {});
|
|
66
|
-
function ignore() {
|
|
67
|
-
}
|
|
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";
|
|
68
30
|
|
|
69
31
|
// src/lib/actions.ts
|
|
70
|
-
var SET_ANNOTATIONS = "SET_ANNOTATIONS";
|
|
71
|
-
var
|
|
72
|
-
var
|
|
73
|
-
var
|
|
74
|
-
var
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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);
|
|
99
121
|
|
|
100
122
|
// src/lib/annotation-plugin.ts
|
|
101
123
|
var AnnotationPlugin = class extends BasePlugin {
|
|
102
|
-
constructor(id, registry, engine) {
|
|
124
|
+
constructor(id, registry, engine, config) {
|
|
103
125
|
super(id, registry);
|
|
126
|
+
this.ANNOTATION_HISTORY_TOPIC = "annotations";
|
|
104
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
|
+
});
|
|
105
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;
|
|
106
145
|
this.coreStore.onAction(SET_DOCUMENT, (_action, state) => {
|
|
107
146
|
const doc = state.core.document;
|
|
108
147
|
if (doc) {
|
|
@@ -111,29 +150,119 @@ var AnnotationPlugin = class extends BasePlugin {
|
|
|
111
150
|
});
|
|
112
151
|
}
|
|
113
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);
|
|
114
205
|
}
|
|
115
206
|
buildCapability() {
|
|
116
207
|
return {
|
|
117
208
|
getPageAnnotations: (options) => {
|
|
118
209
|
return this.getPageAnnotations(options);
|
|
119
210
|
},
|
|
211
|
+
getSelectedAnnotation: () => {
|
|
212
|
+
return getSelectedAnnotation(this.state);
|
|
213
|
+
},
|
|
120
214
|
selectAnnotation: (pageIndex, annotationId) => {
|
|
121
215
|
this.selectAnnotation(pageIndex, annotationId);
|
|
122
216
|
},
|
|
123
217
|
deselectAnnotation: () => {
|
|
124
218
|
this.dispatch(deselectAnnotation());
|
|
125
219
|
},
|
|
126
|
-
|
|
127
|
-
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;
|
|
128
239
|
},
|
|
129
|
-
|
|
130
|
-
this.dispatch(
|
|
240
|
+
setToolDefaults: (subtype, patch) => {
|
|
241
|
+
this.dispatch(updateToolDefaults(subtype, patch));
|
|
131
242
|
},
|
|
132
|
-
|
|
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()
|
|
133
252
|
};
|
|
134
253
|
}
|
|
135
|
-
|
|
136
|
-
|
|
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
|
+
}
|
|
137
266
|
}
|
|
138
267
|
getAllAnnotations(doc) {
|
|
139
268
|
const task = this.engine.getAllAnnotations(doc);
|
|
@@ -143,114 +272,328 @@ var AnnotationPlugin = class extends BasePlugin {
|
|
|
143
272
|
const { pageIndex } = options;
|
|
144
273
|
const doc = this.coreState.core.document;
|
|
145
274
|
if (!doc) {
|
|
146
|
-
|
|
275
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
147
276
|
}
|
|
148
277
|
const page = doc.pages.find((p) => p.index === pageIndex);
|
|
149
278
|
if (!page) {
|
|
150
|
-
|
|
279
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
|
|
151
280
|
}
|
|
152
281
|
return this.engine.getPageAnnotations(doc, page);
|
|
153
282
|
}
|
|
154
283
|
selectAnnotation(pageIndex, annotationId) {
|
|
155
|
-
|
|
156
|
-
|
|
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();
|
|
157
292
|
return;
|
|
158
293
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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);
|
|
163
302
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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;
|
|
168
310
|
}
|
|
169
|
-
|
|
170
|
-
|
|
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;
|
|
171
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);
|
|
172
343
|
const doc = this.coreState.core.document;
|
|
173
|
-
if (!doc)
|
|
174
|
-
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
|
+
}
|
|
175
374
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
}
|
|
182
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;
|
|
183
405
|
}
|
|
184
406
|
};
|
|
185
407
|
AnnotationPlugin.id = "annotation";
|
|
186
408
|
|
|
187
409
|
// src/lib/reducer.ts
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
+
};
|
|
192
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
|
+
});
|
|
193
476
|
var reducer = (state, action) => {
|
|
194
477
|
switch (action.type) {
|
|
195
|
-
|
|
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:
|
|
196
502
|
return {
|
|
197
503
|
...state,
|
|
198
|
-
|
|
199
|
-
// Clear selection if the annotations have changed
|
|
200
|
-
selectedAnnotation: null
|
|
504
|
+
selectedUid: makeUid(action.payload.pageIndex, action.payload.localId)
|
|
201
505
|
};
|
|
202
|
-
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;
|
|
203
512
|
return {
|
|
204
513
|
...state,
|
|
205
|
-
|
|
514
|
+
toolDefaults: {
|
|
515
|
+
...state.toolDefaults,
|
|
516
|
+
[subtype]: { ...state.toolDefaults[subtype], ...patch }
|
|
517
|
+
}
|
|
206
518
|
};
|
|
207
|
-
|
|
519
|
+
}
|
|
520
|
+
/* ───── create ───── */
|
|
521
|
+
case CREATE_ANNOTATION: {
|
|
522
|
+
const { pageIndex, localId, annotation } = action.payload;
|
|
523
|
+
const uid = makeUid(pageIndex, localId);
|
|
208
524
|
return {
|
|
209
525
|
...state,
|
|
210
|
-
|
|
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
|
|
211
532
|
};
|
|
212
|
-
|
|
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;
|
|
213
539
|
return {
|
|
214
540
|
...state,
|
|
215
|
-
|
|
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
|
|
216
550
|
};
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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
|
+
};
|
|
222
565
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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 };
|
|
233
577
|
});
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
const updatedAnnotation = updatedAnnotations.find((a) => a.id === annotationId);
|
|
241
|
-
if (updatedAnnotation) {
|
|
242
|
-
newSelectedAnnotation = {
|
|
243
|
-
...newSelectedAnnotation,
|
|
244
|
-
annotation: updatedAnnotation
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
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;
|
|
248
584
|
return {
|
|
249
585
|
...state,
|
|
250
|
-
|
|
251
|
-
|
|
586
|
+
byUid: {
|
|
587
|
+
...state.byUid,
|
|
588
|
+
[uid]: { ...ta, pdfId, commitState: "synced" }
|
|
589
|
+
}
|
|
252
590
|
};
|
|
253
591
|
}
|
|
592
|
+
case PURGE_ANNOTATION: {
|
|
593
|
+
const { uid } = action.payload;
|
|
594
|
+
const { [uid]: _gone, ...rest } = state.byUid;
|
|
595
|
+
return { ...state, byUid: rest };
|
|
596
|
+
}
|
|
254
597
|
default:
|
|
255
598
|
return state;
|
|
256
599
|
}
|
|
@@ -259,14 +602,22 @@ var reducer = (state, action) => {
|
|
|
259
602
|
// src/lib/index.ts
|
|
260
603
|
var AnnotationPluginPackage = {
|
|
261
604
|
manifest,
|
|
262
|
-
create: (registry, engine) => new AnnotationPlugin(ANNOTATION_PLUGIN_ID, registry, engine),
|
|
605
|
+
create: (registry, engine, config) => new AnnotationPlugin(ANNOTATION_PLUGIN_ID, registry, engine, config),
|
|
263
606
|
reducer,
|
|
264
|
-
initialState
|
|
607
|
+
initialState: (_, config) => initialState(config)
|
|
265
608
|
};
|
|
266
609
|
export {
|
|
267
610
|
ANNOTATION_PLUGIN_ID,
|
|
268
611
|
AnnotationPlugin,
|
|
269
612
|
AnnotationPluginPackage,
|
|
613
|
+
getAnnotations,
|
|
614
|
+
getAnnotationsByPageIndex,
|
|
615
|
+
getSelectedAnnotation,
|
|
616
|
+
getSelectedAnnotationByPageIndex,
|
|
617
|
+
getSelectedAnnotationMode,
|
|
618
|
+
getSelectedAnnotationWithPageIndex,
|
|
619
|
+
isAnnotationSelected,
|
|
620
|
+
isInAnnotationMode,
|
|
270
621
|
manifest
|
|
271
622
|
};
|
|
272
623
|
//# sourceMappingURL=index.js.map
|