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