@embedpdf/plugin-stamp 2.11.0

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.
Files changed (77) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.cjs +2 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +980 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/actions.d.ts +14 -0
  8. package/dist/lib/defaults/name-map.d.ts +2 -0
  9. package/dist/lib/handlers/rubber-stamp.handler.d.ts +4 -0
  10. package/dist/lib/index.d.ts +11 -0
  11. package/dist/lib/manifest.d.ts +4 -0
  12. package/dist/lib/reducer.d.ts +5 -0
  13. package/dist/lib/stamp-plugin.d.ts +54 -0
  14. package/dist/lib/tools.d.ts +58 -0
  15. package/dist/lib/types.d.ts +135 -0
  16. package/dist/preact/adapter.d.ts +5 -0
  17. package/dist/preact/annotation.d.ts +1 -0
  18. package/dist/preact/core.d.ts +1 -0
  19. package/dist/preact/index.cjs +2 -0
  20. package/dist/preact/index.cjs.map +1 -0
  21. package/dist/preact/index.d.ts +1 -0
  22. package/dist/preact/index.js +147 -0
  23. package/dist/preact/index.js.map +1 -0
  24. package/dist/react/adapter.d.ts +2 -0
  25. package/dist/react/annotation.d.ts +1 -0
  26. package/dist/react/core.d.ts +1 -0
  27. package/dist/react/index.cjs +2 -0
  28. package/dist/react/index.cjs.map +1 -0
  29. package/dist/react/index.d.ts +1 -0
  30. package/dist/react/index.js +146 -0
  31. package/dist/react/index.js.map +1 -0
  32. package/dist/shared/components/index.d.ts +3 -0
  33. package/dist/shared/components/stamp-img.d.ts +10 -0
  34. package/dist/shared/components/stamp-preview-renderers.d.ts +2 -0
  35. package/dist/shared/components/stamp-renderer-registration.d.ts +1 -0
  36. package/dist/shared/hooks/index.d.ts +1 -0
  37. package/dist/shared/hooks/use-stamp.d.ts +18 -0
  38. package/dist/shared/index.d.ts +4 -0
  39. package/dist/shared-preact/components/index.d.ts +3 -0
  40. package/dist/shared-preact/components/stamp-img.d.ts +10 -0
  41. package/dist/shared-preact/components/stamp-preview-renderers.d.ts +2 -0
  42. package/dist/shared-preact/components/stamp-renderer-registration.d.ts +1 -0
  43. package/dist/shared-preact/hooks/index.d.ts +1 -0
  44. package/dist/shared-preact/hooks/use-stamp.d.ts +18 -0
  45. package/dist/shared-preact/index.d.ts +4 -0
  46. package/dist/shared-react/components/index.d.ts +3 -0
  47. package/dist/shared-react/components/stamp-img.d.ts +10 -0
  48. package/dist/shared-react/components/stamp-preview-renderers.d.ts +2 -0
  49. package/dist/shared-react/components/stamp-renderer-registration.d.ts +1 -0
  50. package/dist/shared-react/hooks/index.d.ts +1 -0
  51. package/dist/shared-react/hooks/use-stamp.d.ts +18 -0
  52. package/dist/shared-react/index.d.ts +4 -0
  53. package/dist/svelte/components/StampImg.svelte.d.ts +10 -0
  54. package/dist/svelte/components/StampPreview.svelte.d.ts +10 -0
  55. package/dist/svelte/components/StampRendererRegistration.svelte.d.ts +18 -0
  56. package/dist/svelte/components/index.d.ts +2 -0
  57. package/dist/svelte/components/stamp-preview-renderers.d.ts +2 -0
  58. package/dist/svelte/hooks/index.d.ts +1 -0
  59. package/dist/svelte/hooks/use-stamp.svelte.d.ts +23 -0
  60. package/dist/svelte/index.cjs +2 -0
  61. package/dist/svelte/index.cjs.map +1 -0
  62. package/dist/svelte/index.d.ts +4 -0
  63. package/dist/svelte/index.js +223 -0
  64. package/dist/svelte/index.js.map +1 -0
  65. package/dist/vue/components/index.d.ts +2 -0
  66. package/dist/vue/components/stamp-img.vue.d.ts +9 -0
  67. package/dist/vue/components/stamp-preview-renderers.d.ts +2 -0
  68. package/dist/vue/components/stamp-preview.vue.d.ts +10 -0
  69. package/dist/vue/components/stamp-renderer-registration.vue.d.ts +13 -0
  70. package/dist/vue/hooks/index.d.ts +1 -0
  71. package/dist/vue/hooks/use-stamp.d.ts +262 -0
  72. package/dist/vue/index.cjs +2 -0
  73. package/dist/vue/index.cjs.map +1 -0
  74. package/dist/vue/index.d.ts +4 -0
  75. package/dist/vue/index.js +235 -0
  76. package/dist/vue/index.js.map +1 -0
  77. package/package.json +84 -0
package/dist/index.js ADDED
@@ -0,0 +1,980 @@
1
+ import { BasePlugin, createEmitter, createScopedEmitter } from "@embedpdf/core";
2
+ import { PdfAnnotationSubtype, uuidV4, PdfAnnotationName, Task, PdfErrorCode } from "@embedpdf/models";
3
+ import { applyInsertUpright, clampAnnotationToPage, defineAnnotationTool } from "@embedpdf/plugin-annotation";
4
+ const STAMP_PLUGIN_ID = "stamp";
5
+ const manifest = {
6
+ id: STAMP_PLUGIN_ID,
7
+ name: "Stamp Plugin",
8
+ version: "1.0.0",
9
+ provides: ["stamp"],
10
+ requires: ["annotation"],
11
+ optional: ["i18n"],
12
+ defaultConfig: {
13
+ defaultLibrary: {
14
+ id: "custom",
15
+ name: "Custom Stamps",
16
+ nameKey: "stamp.library.custom",
17
+ categories: ["custom", "sidebar"]
18
+ },
19
+ manifests: [
20
+ {
21
+ url: "https://cdn.jsdelivr.net/npm/@embedpdf/default-stamps/{locale}/manifest.json",
22
+ fallbackLocale: "en"
23
+ }
24
+ ]
25
+ }
26
+ };
27
+ const ADD_STAMP_LIBRARY = "STAMP/ADD_LIBRARY";
28
+ const REMOVE_STAMP_LIBRARY = "STAMP/REMOVE_LIBRARY";
29
+ function addStampLibrary(id) {
30
+ return { type: ADD_STAMP_LIBRARY, payload: id };
31
+ }
32
+ function removeStampLibrary(id) {
33
+ return { type: REMOVE_STAMP_LIBRARY, payload: id };
34
+ }
35
+ const rubberStampHandlerFactory = {
36
+ annotationType: PdfAnnotationSubtype.STAMP,
37
+ create(context) {
38
+ const { onPreview, onCommit, getTool, pageSize, pageRotation, getToolContext } = context;
39
+ return {
40
+ onPointerMove: (pos) => {
41
+ const ctx = getToolContext();
42
+ if (!(ctx == null ? void 0 : ctx.stampSize)) return;
43
+ const { width, height } = ctx.stampSize;
44
+ onPreview({
45
+ type: PdfAnnotationSubtype.STAMP,
46
+ bounds: {
47
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
48
+ size: { width, height }
49
+ },
50
+ data: {
51
+ rect: {
52
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
53
+ size: { width, height }
54
+ },
55
+ ghostUrl: ctx.ghostUrl,
56
+ pageRotation
57
+ }
58
+ });
59
+ },
60
+ onPointerDown: (pos) => {
61
+ var _a;
62
+ const tool = getTool();
63
+ const ctx = getToolContext();
64
+ if (!tool || !(ctx == null ? void 0 : ctx.stampSize)) return;
65
+ const { width, height } = ctx.stampSize;
66
+ const rect = {
67
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
68
+ size: { width, height }
69
+ };
70
+ let anno = {
71
+ ...tool.defaults,
72
+ rect,
73
+ type: PdfAnnotationSubtype.STAMP,
74
+ name: ctx.stamp.name ?? tool.defaults.name ?? PdfAnnotationName.Custom,
75
+ subject: ctx.stamp.subject ?? tool.defaults.subject ?? "Custom Stamp",
76
+ flags: tool.defaults.flags ?? ["print"],
77
+ pageIndex: context.pageIndex,
78
+ id: uuidV4(),
79
+ created: /* @__PURE__ */ new Date()
80
+ };
81
+ if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
82
+ anno = applyInsertUpright(anno, pageRotation, false);
83
+ }
84
+ anno = clampAnnotationToPage(anno, pageSize);
85
+ onCommit(anno, { appearance: ctx.appearance });
86
+ onPreview(null);
87
+ },
88
+ onPointerLeave: () => {
89
+ onPreview(null);
90
+ }
91
+ };
92
+ }
93
+ };
94
+ const RUBBER_STAMP_TOOL_ID = "rubberStamp";
95
+ const rubberStampTool = defineAnnotationTool({
96
+ id: RUBBER_STAMP_TOOL_ID,
97
+ name: "Rubber Stamp",
98
+ labelKey: "stamp.rubberStamp",
99
+ categories: ["annotation", "stamp", "insert"],
100
+ matchScore: () => 0,
101
+ interaction: {
102
+ exclusive: true,
103
+ cursor: "copy",
104
+ isDraggable: true,
105
+ isResizable: true,
106
+ isRotatable: false,
107
+ lockAspectRatio: true,
108
+ isGroupDraggable: true,
109
+ isGroupResizable: false,
110
+ isGroupRotatable: false
111
+ },
112
+ defaults: {
113
+ type: PdfAnnotationSubtype.STAMP
114
+ },
115
+ behavior: {
116
+ deactivateToolAfterCreate: true,
117
+ selectAfterCreate: true,
118
+ insertUpright: true,
119
+ useAppearanceStream: true
120
+ },
121
+ pointerHandler: rubberStampHandlerFactory
122
+ });
123
+ const stampTools = [rubberStampTool];
124
+ const ANNOTATION_NAME_MAP = {
125
+ Approved: PdfAnnotationName.Approved,
126
+ Experimental: PdfAnnotationName.Experimental,
127
+ NotApproved: PdfAnnotationName.NotApproved,
128
+ AsIs: PdfAnnotationName.AsIs,
129
+ Expired: PdfAnnotationName.Expired,
130
+ NotForPublicRelease: PdfAnnotationName.NotForPublicRelease,
131
+ Confidential: PdfAnnotationName.Confidential,
132
+ Final: PdfAnnotationName.Final,
133
+ Sold: PdfAnnotationName.Sold,
134
+ Departmental: PdfAnnotationName.Departmental,
135
+ ForComment: PdfAnnotationName.ForComment,
136
+ TopSecret: PdfAnnotationName.TopSecret,
137
+ Draft: PdfAnnotationName.Draft,
138
+ ForPublicRelease: PdfAnnotationName.ForPublicRelease,
139
+ Completed: PdfAnnotationName.Completed,
140
+ Void: PdfAnnotationName.Void,
141
+ PreliminaryResults: PdfAnnotationName.PreliminaryResults,
142
+ InformationOnly: PdfAnnotationName.InformationOnly,
143
+ Rejected: PdfAnnotationName.Rejected,
144
+ Witness: PdfAnnotationName.Witness,
145
+ InitialHere: PdfAnnotationName.InitialHere,
146
+ SignHere: PdfAnnotationName.SignHere,
147
+ Accepted: PdfAnnotationName.Accepted,
148
+ Custom: PdfAnnotationName.Custom,
149
+ Image: PdfAnnotationName.Image
150
+ };
151
+ function parseAnnotationName(name) {
152
+ if (!name) return PdfAnnotationName.Custom;
153
+ return ANNOTATION_NAME_MAP[name] ?? PdfAnnotationName.Custom;
154
+ }
155
+ const _StampPlugin = class _StampPlugin extends BasePlugin {
156
+ constructor(id, registry, config) {
157
+ var _a, _b;
158
+ super(id, registry);
159
+ this.config = config;
160
+ this.instanceId = uuidV4();
161
+ this.libraries = /* @__PURE__ */ new Map();
162
+ this.libraryChange$ = createEmitter();
163
+ this.activeStamp$ = createScopedEmitter((documentId, activeStamp) => ({ documentId, activeStamp }));
164
+ this.managedManifests = [];
165
+ this.annotation = null;
166
+ this.i18n = null;
167
+ this.localeUnsubscribe = null;
168
+ this.toolChangeUnsubscribe = null;
169
+ this.currentGhostUrl = null;
170
+ this.annotation = ((_a = registry.getPlugin("annotation")) == null ? void 0 : _a.provides()) ?? null;
171
+ if (this.annotation) {
172
+ for (const tool of stampTools) {
173
+ this.annotation.addTool(tool);
174
+ }
175
+ this.toolChangeUnsubscribe = this.annotation.onActiveToolChange(({ documentId, tool }) => {
176
+ if ((tool == null ? void 0 : tool.id) !== RUBBER_STAMP_TOOL_ID) {
177
+ if (this.currentGhostUrl) {
178
+ URL.revokeObjectURL(this.currentGhostUrl);
179
+ this.currentGhostUrl = null;
180
+ }
181
+ this.activeStamp$.emit(documentId, null);
182
+ }
183
+ });
184
+ }
185
+ this.i18n = ((_b = registry.getPlugin("i18n")) == null ? void 0 : _b.provides()) ?? null;
186
+ }
187
+ async initialize() {
188
+ if (this.config.libraries) {
189
+ for (const libConfig of this.config.libraries) {
190
+ await this.loadLibraryInternal(libConfig).toPromise();
191
+ }
192
+ }
193
+ if (this.config.manifests && this.config.manifests.length > 0) {
194
+ await this.initializeManifests();
195
+ }
196
+ }
197
+ buildCapability() {
198
+ return {
199
+ getLibraries: () => this.getLibraries(),
200
+ getStampsByCategory: (category) => this.getStampsByCategory(category),
201
+ renderStamp: (libraryId, pageIndex, width, dpr) => this.renderStamp(libraryId, pageIndex, width, dpr),
202
+ loadLibrary: (config) => this.loadLibrary(config),
203
+ loadLibraryFromManifest: (url) => this.loadLibraryFromManifest(url),
204
+ createNewLibrary: (name, options) => this.createNewLibrary(name, options),
205
+ addStampToLibrary: (libraryId, stamp, pdf) => this.addStampToLibrary(libraryId, stamp, pdf),
206
+ removeStampFromLibrary: (libraryId, stampId) => this.removeStampFromLibrary(libraryId, stampId),
207
+ updateStamp: (libraryId, stampId, updates) => this.updateStamp(libraryId, stampId, updates),
208
+ updateLibrary: (libraryId, updates) => this.updateLibrary(libraryId, updates),
209
+ removeLibrary: (id) => this.removeLibrary(id),
210
+ exportLibrary: (id) => this.exportLibrary(id),
211
+ forDocument: (documentId) => this.createStampScope(documentId),
212
+ onActiveStampChange: this.activeStamp$.onGlobal,
213
+ onLibraryChange: this.libraryChange$.on
214
+ };
215
+ }
216
+ getLibraries() {
217
+ return Array.from(this.libraries.values());
218
+ }
219
+ getStampsByCategory(category) {
220
+ var _a, _b;
221
+ const results = [];
222
+ for (const library of this.libraries.values()) {
223
+ const libraryMatches = ((_a = library.categories) == null ? void 0 : _a.includes(category)) ?? false;
224
+ for (const stamp of library.stamps) {
225
+ const stampMatches = ((_b = stamp.categories) == null ? void 0 : _b.includes(category)) ?? false;
226
+ if (libraryMatches || stampMatches) {
227
+ results.push({ library, stamp });
228
+ }
229
+ }
230
+ }
231
+ return results;
232
+ }
233
+ renderStamp(libraryId, pageIndex, width, dpr) {
234
+ const library = this.libraries.get(libraryId);
235
+ if (!library) {
236
+ const task = new Task();
237
+ task.reject({
238
+ code: PdfErrorCode.NotFound,
239
+ message: `Stamp library not found: ${libraryId}`
240
+ });
241
+ return task;
242
+ }
243
+ const page = library.document.pages[pageIndex];
244
+ if (!page) {
245
+ const task = new Task();
246
+ task.reject({
247
+ code: PdfErrorCode.NotFound,
248
+ message: `Page ${pageIndex} not found in stamp library: ${libraryId}`
249
+ });
250
+ return task;
251
+ }
252
+ const scaleFactor = width / page.size.width;
253
+ return this.engine.renderPageRect(
254
+ library.document,
255
+ page,
256
+ { origin: { x: 0, y: 0 }, size: page.size },
257
+ {
258
+ scaleFactor,
259
+ dpr: dpr ?? 1,
260
+ withAnnotations: true,
261
+ rotation: page.rotation,
262
+ transparentBackground: true
263
+ }
264
+ );
265
+ }
266
+ loadLibrary(config) {
267
+ return this.loadLibraryInternal(config);
268
+ }
269
+ createNewLibrary(name, options) {
270
+ const task = new Task();
271
+ const libraryId = (options == null ? void 0 : options.id) ?? this.generateLibraryId();
272
+ const documentId = this.getLibraryDocumentId(libraryId);
273
+ this.engine.createDocument(documentId).wait(
274
+ (doc) => {
275
+ const library = {
276
+ id: libraryId,
277
+ name,
278
+ nameKey: options == null ? void 0 : options.nameKey,
279
+ document: doc,
280
+ stamps: [],
281
+ categories: options == null ? void 0 : options.categories,
282
+ readonly: (options == null ? void 0 : options.readonly) ?? false
283
+ };
284
+ this.libraries.set(libraryId, library);
285
+ this.dispatch(addStampLibrary(libraryId));
286
+ this.emitLibraryChange();
287
+ task.resolve(libraryId);
288
+ },
289
+ (error) => {
290
+ this.logger.error(
291
+ "StampPlugin",
292
+ "CreateNewLibrary",
293
+ `Failed to create library: ${name}`,
294
+ error
295
+ );
296
+ task.fail(error);
297
+ }
298
+ );
299
+ return task;
300
+ }
301
+ addStampToLibrary(libraryId, stamp, pdf) {
302
+ const task = new Task();
303
+ const library = this.libraries.get(libraryId);
304
+ if (!library) {
305
+ task.reject({
306
+ code: PdfErrorCode.NotFound,
307
+ message: `Stamp library not found: ${libraryId}`
308
+ });
309
+ return task;
310
+ }
311
+ if (library.readonly) {
312
+ task.reject({
313
+ code: PdfErrorCode.NotSupport,
314
+ message: `Cannot add stamps to readonly library: ${libraryId}`
315
+ });
316
+ return task;
317
+ }
318
+ const tempDocId = `stamp-temp-${Date.now()}`;
319
+ this.engine.openDocumentBuffer({ id: tempDocId, content: pdf }).wait(
320
+ (tempDoc) => {
321
+ this.engine.importPages(library.document, tempDoc, [0]).wait(
322
+ (newPages) => {
323
+ const newPage = newPages[0];
324
+ library.document.pages.push(newPage);
325
+ library.document.pageCount = library.document.pages.length;
326
+ const stampDef = {
327
+ ...stamp,
328
+ id: uuidV4(),
329
+ pageIndex: newPage.index
330
+ };
331
+ library.stamps.push(stampDef);
332
+ this.engine.closeDocument(tempDoc).wait(
333
+ () => {
334
+ this.emitLibraryChange();
335
+ task.resolve();
336
+ },
337
+ () => {
338
+ this.emitLibraryChange();
339
+ task.resolve();
340
+ }
341
+ );
342
+ },
343
+ (error) => {
344
+ this.logger.error("StampPlugin", "AddStampToLibrary", "Failed to import page", error);
345
+ this.engine.closeDocument(tempDoc).wait(
346
+ () => task.fail(error),
347
+ () => task.fail(error)
348
+ );
349
+ }
350
+ );
351
+ },
352
+ (error) => {
353
+ this.logger.error(
354
+ "StampPlugin",
355
+ "AddStampToLibrary",
356
+ "Failed to open temp document",
357
+ error
358
+ );
359
+ task.fail(error);
360
+ }
361
+ );
362
+ return task;
363
+ }
364
+ createStampScope(documentId) {
365
+ return {
366
+ createStampFromAnnotation: (annotation, stamp, libraryId) => this.createStampFromAnnotation(documentId, annotation, stamp, libraryId),
367
+ createStampFromAnnotations: (annotations, stamp, libraryId) => this.createStampFromAnnotations(documentId, annotations, stamp, libraryId),
368
+ activateStampPlacement: (libraryId, stamp) => this.activateStampPlacement(documentId, libraryId, stamp),
369
+ activateStampPlacementById: (libraryId, stampId) => this.activateStampPlacementById(documentId, libraryId, stampId),
370
+ getActiveStamp: () => this.getActiveStamp(documentId),
371
+ onActiveStampChange: this.activeStamp$.forScope(documentId)
372
+ };
373
+ }
374
+ activateStampPlacementById(documentId, libraryId, stampId) {
375
+ const task = new Task();
376
+ const library = this.libraries.get(libraryId);
377
+ if (!library) {
378
+ task.reject({
379
+ code: PdfErrorCode.NotFound,
380
+ message: `Stamp library not found: ${libraryId}`
381
+ });
382
+ return task;
383
+ }
384
+ const stamp = library.stamps.find((s) => s.id === stampId);
385
+ if (!stamp) {
386
+ task.reject({
387
+ code: PdfErrorCode.NotFound,
388
+ message: `Stamp ${stampId} not found in library: ${libraryId}`
389
+ });
390
+ return task;
391
+ }
392
+ return this.activateStampPlacement(documentId, libraryId, stamp);
393
+ }
394
+ getActiveStamp(documentId) {
395
+ return this.activeStamp$.getValue(documentId) ?? null;
396
+ }
397
+ activateStampPlacement(documentId, libraryId, stamp) {
398
+ const task = new Task();
399
+ const library = this.libraries.get(libraryId);
400
+ if (!library) {
401
+ task.reject({
402
+ code: PdfErrorCode.NotFound,
403
+ message: `Stamp library not found: ${libraryId}`
404
+ });
405
+ return task;
406
+ }
407
+ const page = library.document.pages[stamp.pageIndex];
408
+ if (!page) {
409
+ task.reject({
410
+ code: PdfErrorCode.NotFound,
411
+ message: `Page ${stamp.pageIndex} not found in library`
412
+ });
413
+ return task;
414
+ }
415
+ const stampSize = page.size;
416
+ this.engine.extractPages(library.document, [stamp.pageIndex]).wait(
417
+ (appearance) => {
418
+ this.renderStamp(libraryId, stamp.pageIndex, stampSize.width, 2).wait(
419
+ (blob) => {
420
+ var _a;
421
+ if (this.currentGhostUrl) {
422
+ URL.revokeObjectURL(this.currentGhostUrl);
423
+ }
424
+ const ghostUrl = URL.createObjectURL(blob);
425
+ this.currentGhostUrl = ghostUrl;
426
+ (_a = this.annotation) == null ? void 0 : _a.setActiveTool(RUBBER_STAMP_TOOL_ID, {
427
+ appearance,
428
+ ghostUrl,
429
+ stampSize,
430
+ libraryId,
431
+ stamp
432
+ });
433
+ this.activeStamp$.emit(documentId, { libraryId, stamp });
434
+ task.resolve();
435
+ },
436
+ (error) => {
437
+ this.logger.error(
438
+ "StampPlugin",
439
+ "ActivateStampPlacement",
440
+ "Failed to render stamp preview",
441
+ error
442
+ );
443
+ task.fail(error);
444
+ }
445
+ );
446
+ },
447
+ (error) => {
448
+ this.logger.error(
449
+ "StampPlugin",
450
+ "ActivateStampPlacement",
451
+ "Failed to extract stamp page",
452
+ error
453
+ );
454
+ task.fail(error);
455
+ }
456
+ );
457
+ return task;
458
+ }
459
+ createStampFromAnnotation(documentId, annotation, stamp, libraryId) {
460
+ const task = new Task();
461
+ const docState = this.getCoreDocument(documentId);
462
+ if (!(docState == null ? void 0 : docState.document)) {
463
+ task.reject({ code: PdfErrorCode.DocNotOpen, message: "document is not open" });
464
+ return task;
465
+ }
466
+ const doc = docState.document;
467
+ const page = doc.pages[annotation.pageIndex];
468
+ if (!page) {
469
+ task.reject({
470
+ code: PdfErrorCode.NotFound,
471
+ message: `page ${annotation.pageIndex} not found`
472
+ });
473
+ return task;
474
+ }
475
+ this.resolveTargetLibrary(libraryId).wait(
476
+ (targetId) => {
477
+ this.engine.exportAnnotationAppearanceAsPdf(doc, page, annotation).wait(
478
+ (pdf) => {
479
+ this.addStampToLibrary(targetId, stamp, pdf).wait(
480
+ () => task.resolve(),
481
+ (error) => task.fail(error)
482
+ );
483
+ },
484
+ (error) => {
485
+ this.logger.error(
486
+ "StampPlugin",
487
+ "CreateStampFromAnnotation",
488
+ "Failed to export annotation",
489
+ error
490
+ );
491
+ task.fail(error);
492
+ }
493
+ );
494
+ },
495
+ (error) => task.fail(error)
496
+ );
497
+ return task;
498
+ }
499
+ createStampFromAnnotations(documentId, annotations, stamp, libraryId) {
500
+ const task = new Task();
501
+ if (annotations.length === 0) {
502
+ task.reject({ code: PdfErrorCode.NotFound, message: "no annotations provided" });
503
+ return task;
504
+ }
505
+ const docState = this.getCoreDocument(documentId);
506
+ if (!(docState == null ? void 0 : docState.document)) {
507
+ task.reject({ code: PdfErrorCode.DocNotOpen, message: "document is not open" });
508
+ return task;
509
+ }
510
+ const doc = docState.document;
511
+ const page = doc.pages[annotations[0].pageIndex];
512
+ if (!page) {
513
+ task.reject({
514
+ code: PdfErrorCode.NotFound,
515
+ message: `page ${annotations[0].pageIndex} not found`
516
+ });
517
+ return task;
518
+ }
519
+ this.resolveTargetLibrary(libraryId).wait(
520
+ (targetId) => {
521
+ this.engine.exportAnnotationsAppearanceAsPdf(doc, page, annotations).wait(
522
+ (pdf) => {
523
+ this.addStampToLibrary(targetId, stamp, pdf).wait(
524
+ () => task.resolve(),
525
+ (error) => task.fail(error)
526
+ );
527
+ },
528
+ (error) => {
529
+ this.logger.error(
530
+ "StampPlugin",
531
+ "CreateStampFromAnnotations",
532
+ "Failed to export annotations",
533
+ error
534
+ );
535
+ task.fail(error);
536
+ }
537
+ );
538
+ },
539
+ (error) => task.fail(error)
540
+ );
541
+ return task;
542
+ }
543
+ resolveTargetLibrary(libraryId) {
544
+ if (libraryId && this.libraries.has(libraryId)) {
545
+ const task = new Task();
546
+ task.resolve(libraryId);
547
+ return task;
548
+ }
549
+ const defaults = this.config.defaultLibrary;
550
+ if (this.config.defaultLibrary === false || !defaults) {
551
+ const task = new Task();
552
+ task.reject({
553
+ code: PdfErrorCode.NotSupport,
554
+ message: "Default library creation is disabled"
555
+ });
556
+ return task;
557
+ }
558
+ const defaultId = defaults.id ?? "custom";
559
+ if (this.libraries.has(defaultId)) {
560
+ const task = new Task();
561
+ task.resolve(defaultId);
562
+ return task;
563
+ }
564
+ return this.createNewLibrary(defaults.name ?? "Custom Stamps", {
565
+ id: defaultId,
566
+ nameKey: defaults.nameKey,
567
+ categories: defaults.categories
568
+ });
569
+ }
570
+ removeLibrary(id) {
571
+ const task = new Task();
572
+ const library = this.libraries.get(id);
573
+ if (!library) {
574
+ task.resolve();
575
+ return task;
576
+ }
577
+ this.engine.closeDocument(library.document).wait(
578
+ () => {
579
+ this.libraries.delete(id);
580
+ this.dispatch(removeStampLibrary(id));
581
+ this.emitLibraryChange();
582
+ task.resolve();
583
+ },
584
+ () => {
585
+ this.logger.warn(
586
+ "StampPlugin",
587
+ "RemoveLibrary",
588
+ `Failed to close document for library: ${id}`
589
+ );
590
+ this.libraries.delete(id);
591
+ this.dispatch(removeStampLibrary(id));
592
+ this.emitLibraryChange();
593
+ task.resolve();
594
+ }
595
+ );
596
+ return task;
597
+ }
598
+ exportLibrary(id) {
599
+ const task = new Task();
600
+ const library = this.libraries.get(id);
601
+ if (!library) {
602
+ task.reject({
603
+ code: PdfErrorCode.NotFound,
604
+ message: `Stamp library not found: ${id}`
605
+ });
606
+ return task;
607
+ }
608
+ this.engine.saveAsCopy(library.document).wait(
609
+ (pdf) => {
610
+ task.resolve({
611
+ name: library.name,
612
+ pdf,
613
+ stamps: library.stamps,
614
+ categories: library.categories
615
+ });
616
+ },
617
+ (error) => {
618
+ this.logger.error("StampPlugin", "ExportLibrary", `Failed to export library: ${id}`, error);
619
+ task.fail(error);
620
+ }
621
+ );
622
+ return task;
623
+ }
624
+ loadLibraryInternal(config) {
625
+ const task = new Task();
626
+ const libraryId = config.id ?? this.generateLibraryId();
627
+ const documentId = this.getLibraryDocumentId(libraryId);
628
+ const engineTask = typeof config.pdf === "string" ? this.engine.openDocumentUrl({ id: documentId, url: config.pdf }) : this.engine.openDocumentBuffer({ id: documentId, content: config.pdf });
629
+ engineTask.wait(
630
+ (doc) => {
631
+ const stamps = config.stamps.map((s) => s.id ? s : { ...s, id: uuidV4() });
632
+ const library = {
633
+ id: libraryId,
634
+ name: config.name,
635
+ nameKey: config.nameKey,
636
+ document: doc,
637
+ stamps,
638
+ categories: config.categories,
639
+ readonly: config.readonly ?? false
640
+ };
641
+ this.libraries.set(libraryId, library);
642
+ this.dispatch(addStampLibrary(libraryId));
643
+ this.emitLibraryChange();
644
+ task.resolve(libraryId);
645
+ },
646
+ (error) => {
647
+ this.logger.error(
648
+ "StampPlugin",
649
+ "LoadLibrary",
650
+ `Failed to load stamp library: ${config.name}`,
651
+ error
652
+ );
653
+ task.fail(error);
654
+ }
655
+ );
656
+ return task;
657
+ }
658
+ removeStampFromLibrary(libraryId, stampId) {
659
+ const task = new Task();
660
+ const library = this.libraries.get(libraryId);
661
+ if (!library) {
662
+ task.reject({
663
+ code: PdfErrorCode.NotFound,
664
+ message: `Stamp library not found: ${libraryId}`
665
+ });
666
+ return task;
667
+ }
668
+ if (library.readonly) {
669
+ task.reject({
670
+ code: PdfErrorCode.NotSupport,
671
+ message: `Cannot remove stamps from readonly library: ${libraryId}`
672
+ });
673
+ return task;
674
+ }
675
+ const stampIdx = library.stamps.findIndex((s) => s.id === stampId);
676
+ if (stampIdx === -1) {
677
+ task.reject({
678
+ code: PdfErrorCode.NotFound,
679
+ message: `Stamp ${stampId} not found in library: ${libraryId}`
680
+ });
681
+ return task;
682
+ }
683
+ const pageIndex = library.stamps[stampIdx].pageIndex;
684
+ this.engine.deletePage(library.document, pageIndex).wait(
685
+ () => {
686
+ library.stamps.splice(stampIdx, 1);
687
+ library.document.pages.splice(pageIndex, 1);
688
+ library.document.pageCount = library.document.pages.length;
689
+ for (const s of library.stamps) {
690
+ if (s.pageIndex > pageIndex) {
691
+ s.pageIndex--;
692
+ }
693
+ }
694
+ for (let i = 0; i < library.document.pages.length; i++) {
695
+ library.document.pages[i].index = i;
696
+ }
697
+ this.emitLibraryChange();
698
+ task.resolve();
699
+ },
700
+ (error) => {
701
+ this.logger.error(
702
+ "StampPlugin",
703
+ "RemoveStampFromLibrary",
704
+ `Failed to delete page for stamp ${stampId}`,
705
+ error
706
+ );
707
+ task.fail(error);
708
+ }
709
+ );
710
+ return task;
711
+ }
712
+ updateStamp(libraryId, stampId, updates) {
713
+ const task = new Task();
714
+ const library = this.libraries.get(libraryId);
715
+ if (!library) {
716
+ task.reject({
717
+ code: PdfErrorCode.NotFound,
718
+ message: `Stamp library not found: ${libraryId}`
719
+ });
720
+ return task;
721
+ }
722
+ if (library.readonly) {
723
+ task.reject({
724
+ code: PdfErrorCode.NotSupport,
725
+ message: `Cannot update stamps in readonly library: ${libraryId}`
726
+ });
727
+ return task;
728
+ }
729
+ const stamp = library.stamps.find((s) => s.id === stampId);
730
+ if (!stamp) {
731
+ task.reject({
732
+ code: PdfErrorCode.NotFound,
733
+ message: `Stamp ${stampId} not found in library: ${libraryId}`
734
+ });
735
+ return task;
736
+ }
737
+ Object.assign(stamp, updates);
738
+ this.emitLibraryChange();
739
+ task.resolve();
740
+ return task;
741
+ }
742
+ updateLibrary(libraryId, updates) {
743
+ const task = new Task();
744
+ const library = this.libraries.get(libraryId);
745
+ if (!library) {
746
+ task.reject({
747
+ code: PdfErrorCode.NotFound,
748
+ message: `Stamp library not found: ${libraryId}`
749
+ });
750
+ return task;
751
+ }
752
+ if (library.readonly) {
753
+ task.reject({
754
+ code: PdfErrorCode.NotSupport,
755
+ message: `Cannot update readonly library: ${libraryId}`
756
+ });
757
+ return task;
758
+ }
759
+ if (updates.name !== void 0) library.name = updates.name;
760
+ if (updates.nameKey !== void 0) library.nameKey = updates.nameKey;
761
+ if (updates.categories !== void 0) library.categories = updates.categories;
762
+ if (updates.readonly !== void 0) library.readonly = updates.readonly;
763
+ this.emitLibraryChange();
764
+ task.resolve();
765
+ return task;
766
+ }
767
+ loadLibraryFromManifest(url) {
768
+ return this.loadManifestUrl(url, true);
769
+ }
770
+ async initializeManifests() {
771
+ var _a;
772
+ const currentLocale = ((_a = this.i18n) == null ? void 0 : _a.getLocale()) ?? "en";
773
+ for (const source of this.config.manifests) {
774
+ const localeAware = source.url.includes("{locale}");
775
+ const locale = localeAware ? currentLocale : "";
776
+ const resolvedUrl = source.url.replace("{locale}", locale || currentLocale);
777
+ const managed = {
778
+ source,
779
+ localeAware,
780
+ currentLocale: localeAware ? currentLocale : "",
781
+ libraryId: null
782
+ };
783
+ this.managedManifests.push(managed);
784
+ try {
785
+ const libraryId = await this.loadManifestUrl(resolvedUrl, true).toPromise();
786
+ managed.libraryId = libraryId;
787
+ } catch {
788
+ if (localeAware && source.fallbackLocale && source.fallbackLocale !== currentLocale) {
789
+ const fallbackUrl = source.url.replace("{locale}", source.fallbackLocale);
790
+ try {
791
+ const libraryId = await this.loadManifestUrl(fallbackUrl, true).toPromise();
792
+ managed.libraryId = libraryId;
793
+ managed.currentLocale = source.fallbackLocale;
794
+ } catch {
795
+ this.logger.warn(
796
+ "StampPlugin",
797
+ "InitManifests",
798
+ `Failed to load manifest (including fallback): ${source.url}`
799
+ );
800
+ }
801
+ } else {
802
+ this.logger.warn(
803
+ "StampPlugin",
804
+ "InitManifests",
805
+ `Failed to load manifest: ${resolvedUrl}`
806
+ );
807
+ }
808
+ }
809
+ }
810
+ if (this.i18n) {
811
+ this.localeUnsubscribe = this.i18n.onLocaleChange((event) => {
812
+ this.handleLocaleChange(event.currentLocale);
813
+ });
814
+ }
815
+ }
816
+ async handleLocaleChange(newLocale) {
817
+ for (const managed of this.managedManifests) {
818
+ if (!managed.localeAware || managed.currentLocale === newLocale) {
819
+ continue;
820
+ }
821
+ if (managed.libraryId) {
822
+ try {
823
+ await this.removeLibrary(managed.libraryId).toPromise();
824
+ } catch {
825
+ }
826
+ managed.libraryId = null;
827
+ }
828
+ const resolvedUrl = managed.source.url.replace("{locale}", newLocale);
829
+ try {
830
+ const libraryId = await this.loadManifestUrl(resolvedUrl, true).toPromise();
831
+ managed.libraryId = libraryId;
832
+ managed.currentLocale = newLocale;
833
+ } catch {
834
+ const fallback = managed.source.fallbackLocale ?? "en";
835
+ if (fallback !== newLocale) {
836
+ const fallbackUrl = managed.source.url.replace("{locale}", fallback);
837
+ try {
838
+ const libraryId = await this.loadManifestUrl(fallbackUrl, true).toPromise();
839
+ managed.libraryId = libraryId;
840
+ managed.currentLocale = fallback;
841
+ } catch {
842
+ this.logger.warn(
843
+ "StampPlugin",
844
+ "LocaleChange",
845
+ `Failed to load manifest for locale ${newLocale} (including fallback): ${managed.source.url}`
846
+ );
847
+ }
848
+ } else {
849
+ this.logger.warn(
850
+ "StampPlugin",
851
+ "LocaleChange",
852
+ `Failed to load manifest for locale ${newLocale}: ${managed.source.url}`
853
+ );
854
+ }
855
+ }
856
+ }
857
+ }
858
+ loadManifestUrl(url, readonly) {
859
+ const task = new Task();
860
+ fetch(url).then((response) => {
861
+ if (!response.ok) {
862
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
863
+ }
864
+ return response.json();
865
+ }).then((manifest2) => {
866
+ const pdfUrl = this.resolveManifestPdfUrl(url, manifest2.pdf);
867
+ const stamps = manifest2.stamps.map((entry) => ({
868
+ id: entry.id ?? uuidV4(),
869
+ pageIndex: entry.pageIndex,
870
+ name: parseAnnotationName(entry.name),
871
+ subject: entry.subject,
872
+ subjectKey: entry.subjectKey,
873
+ label: entry.label,
874
+ categories: entry.categories
875
+ }));
876
+ const config = {
877
+ id: manifest2.id,
878
+ name: manifest2.name,
879
+ nameKey: manifest2.nameKey,
880
+ pdf: pdfUrl,
881
+ stamps,
882
+ categories: manifest2.categories,
883
+ readonly
884
+ };
885
+ this.loadLibraryInternal(config).wait(
886
+ (libraryId) => task.resolve(libraryId),
887
+ (error) => task.fail(error)
888
+ );
889
+ }).catch((error) => {
890
+ this.logger.error("StampPlugin", "LoadManifest", `Failed to fetch manifest: ${url}`, error);
891
+ task.reject({
892
+ code: PdfErrorCode.NotFound,
893
+ message: `Failed to load stamp manifest: ${url}`
894
+ });
895
+ });
896
+ return task;
897
+ }
898
+ resolveManifestPdfUrl(manifestUrl, pdfPath) {
899
+ if (pdfPath.startsWith("http://") || pdfPath.startsWith("https://") || pdfPath.startsWith("/")) {
900
+ return pdfPath;
901
+ }
902
+ const base = manifestUrl.substring(0, manifestUrl.lastIndexOf("/") + 1);
903
+ return base + pdfPath;
904
+ }
905
+ generateLibraryId() {
906
+ return uuidV4();
907
+ }
908
+ getLibraryDocumentId(libraryId) {
909
+ return `stamp-doc-${this.instanceId}-${libraryId}`;
910
+ }
911
+ emitLibraryChange() {
912
+ this.libraryChange$.emit(this.getLibraries());
913
+ }
914
+ async destroy() {
915
+ if (this.currentGhostUrl) {
916
+ URL.revokeObjectURL(this.currentGhostUrl);
917
+ this.currentGhostUrl = null;
918
+ }
919
+ if (this.toolChangeUnsubscribe) {
920
+ this.toolChangeUnsubscribe();
921
+ this.toolChangeUnsubscribe = null;
922
+ }
923
+ if (this.localeUnsubscribe) {
924
+ this.localeUnsubscribe();
925
+ this.localeUnsubscribe = null;
926
+ }
927
+ this.activeStamp$.clear();
928
+ const libs = Array.from(this.libraries.values());
929
+ for (const library of libs) {
930
+ try {
931
+ await this.engine.closeDocument(library.document).toPromise();
932
+ } catch {
933
+ }
934
+ }
935
+ this.libraries.clear();
936
+ this.managedManifests.length = 0;
937
+ super.destroy();
938
+ }
939
+ };
940
+ _StampPlugin.id = STAMP_PLUGIN_ID;
941
+ let StampPlugin = _StampPlugin;
942
+ const initialState = {
943
+ libraryIds: []
944
+ };
945
+ const stampReducer = (state = initialState, action) => {
946
+ switch (action.type) {
947
+ case ADD_STAMP_LIBRARY:
948
+ return {
949
+ ...state,
950
+ libraryIds: [...state.libraryIds, action.payload]
951
+ };
952
+ case REMOVE_STAMP_LIBRARY:
953
+ return {
954
+ ...state,
955
+ libraryIds: state.libraryIds.filter((id) => id !== action.payload)
956
+ };
957
+ default:
958
+ return state;
959
+ }
960
+ };
961
+ const StampPluginPackage = {
962
+ manifest,
963
+ create: (registry, config) => new StampPlugin(STAMP_PLUGIN_ID, registry, config),
964
+ reducer: stampReducer,
965
+ initialState
966
+ };
967
+ export {
968
+ ADD_STAMP_LIBRARY,
969
+ REMOVE_STAMP_LIBRARY,
970
+ RUBBER_STAMP_TOOL_ID,
971
+ STAMP_PLUGIN_ID,
972
+ StampPlugin,
973
+ StampPluginPackage,
974
+ addStampLibrary,
975
+ initialState,
976
+ manifest,
977
+ removeStampLibrary,
978
+ stampReducer
979
+ };
980
+ //# sourceMappingURL=index.js.map