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