@embedpdf/plugin-annotation 2.11.1 → 2.12.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.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { clamp, BasePlugin, createScopedEmitter, createBehaviorEmitter } from "@embedpdf/core";
2
- import { PdfAnnotationSubtype, PdfAnnotationReplyType, expandRect, rectFromPoints, uuidV4, PdfAnnotationLineEnding, getRectCenter, inferRotationCenterFromRects, calculateRotatedRectAABBAroundPoint, rotateAndTranslatePoint, calculateRotatedRectAABB, rotatePointAround, rotateVertices, PdfAnnotationName, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, PdfAnnotationBorderStyle, PdfBlendMode, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task, PdfActionType, PdfZoomMode, TaskStage } from "@embedpdf/models";
2
+ import { PdfAnnotationSubtype, PdfAnnotationReplyType, expandRect, rectFromPoints, uuidV4, PdfAnnotationLineEnding, getRectCenter, inferRotationCenterFromRects, calculateRotatedRectAABBAroundPoint, rotateAndTranslatePoint, calculateRotatedRectAABB, rotatePointAround, rotateVertices, PdfAnnotationName, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, PdfAnnotationBorderStyle, getImageMetadata, fitSizeWithin, PdfBlendMode, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task, PdfActionType, PdfZoomMode, TaskStage } from "@embedpdf/models";
3
3
  import { calculateRotatedRectAABB as calculateRotatedRectAABB2, calculateRotatedRectAABBAroundPoint as calculateRotatedRectAABBAroundPoint2, getRectCenter as getRectCenter2, inferRotationCenterFromRects as inferRotationCenterFromRects2, rotatePointAround as rotatePointAround2, rotateVertices as rotateVertices2 } from "@embedpdf/models";
4
4
  const ANNOTATION_PLUGIN_ID = "annotation";
5
5
  const manifest = {
@@ -2201,62 +2201,125 @@ const squareHandlerFactory = {
2201
2201
  };
2202
2202
  }
2203
2203
  };
2204
+ const imageFetchCache = /* @__PURE__ */ new Map();
2204
2205
  const stampHandlerFactory = {
2205
2206
  annotationType: PdfAnnotationSubtype.STAMP,
2206
2207
  create(context) {
2207
- const { services, onCommit, getTool, pageSize, pageRotation } = context;
2208
+ const { services, onCommit, onPreview, getTool, pageSize, pageRotation } = context;
2209
+ let cachedBuffer = null;
2210
+ let cachedSize = null;
2211
+ const commitStamp = (pos, width, height, ctx) => {
2212
+ var _a;
2213
+ const tool = getTool();
2214
+ if (!tool) return;
2215
+ const rect = {
2216
+ origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
2217
+ size: { width, height }
2218
+ };
2219
+ let anno = {
2220
+ ...tool.defaults,
2221
+ rect,
2222
+ type: PdfAnnotationSubtype.STAMP,
2223
+ name: tool.defaults.name ?? PdfAnnotationName.Image,
2224
+ subject: tool.defaults.subject ?? "Stamp",
2225
+ flags: tool.defaults.flags ?? ["print"],
2226
+ pageIndex: context.pageIndex,
2227
+ id: uuidV4(),
2228
+ created: /* @__PURE__ */ new Date()
2229
+ };
2230
+ if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
2231
+ anno = applyInsertUpright(anno, pageRotation, false);
2232
+ }
2233
+ anno = clampAnnotationToPage(anno, pageSize);
2234
+ onCommit(anno, ctx);
2235
+ };
2236
+ const commitFromBuffer = (pos, buffer, imageSize) => {
2237
+ const meta = getImageMetadata(buffer);
2238
+ if (!meta || meta.mimeType === "application/pdf") return false;
2239
+ const fitted = fitSizeWithin(meta, pageSize);
2240
+ const width = (imageSize == null ? void 0 : imageSize.width) ?? fitted.width;
2241
+ const height = (imageSize == null ? void 0 : imageSize.height) ?? fitted.height;
2242
+ commitStamp(pos, width, height, { data: buffer });
2243
+ return true;
2244
+ };
2208
2245
  return {
2246
+ onHandlerActiveStart: () => {
2247
+ const tool = getTool();
2248
+ const imageSrc = tool == null ? void 0 : tool.defaults.imageSrc;
2249
+ if (!imageSrc) return;
2250
+ let entry = imageFetchCache.get(imageSrc);
2251
+ if (!entry) {
2252
+ const promise = fetch(imageSrc).then((res) => res.arrayBuffer()).then((buffer) => {
2253
+ const meta = getImageMetadata(buffer);
2254
+ if (!meta || meta.mimeType === "application/pdf") return null;
2255
+ const fitted = fitSizeWithin(meta, pageSize);
2256
+ const imageSize = tool.defaults.imageSize;
2257
+ return {
2258
+ buffer,
2259
+ width: (imageSize == null ? void 0 : imageSize.width) ?? fitted.width,
2260
+ height: (imageSize == null ? void 0 : imageSize.height) ?? fitted.height
2261
+ };
2262
+ }).catch(() => null);
2263
+ entry = { promise, refs: 1 };
2264
+ imageFetchCache.set(imageSrc, entry);
2265
+ } else {
2266
+ entry.refs++;
2267
+ }
2268
+ entry.promise.then((result) => {
2269
+ if (!result) return;
2270
+ cachedBuffer = result.buffer;
2271
+ cachedSize = { width: result.width, height: result.height };
2272
+ });
2273
+ },
2274
+ onHandlerActiveEnd: () => {
2275
+ const tool = getTool();
2276
+ const imageSrc = tool == null ? void 0 : tool.defaults.imageSrc;
2277
+ if (imageSrc) {
2278
+ const entry = imageFetchCache.get(imageSrc);
2279
+ if (entry && --entry.refs <= 0) {
2280
+ imageFetchCache.delete(imageSrc);
2281
+ }
2282
+ }
2283
+ cachedBuffer = null;
2284
+ cachedSize = null;
2285
+ onPreview(null);
2286
+ },
2287
+ onPointerMove: (pos) => {
2288
+ var _a;
2289
+ const tool = getTool();
2290
+ if (!((_a = tool == null ? void 0 : tool.behavior) == null ? void 0 : _a.showGhost) || !cachedSize || !tool.defaults.imageSrc) return;
2291
+ const rect = {
2292
+ origin: { x: pos.x - cachedSize.width / 2, y: pos.y - cachedSize.height / 2 },
2293
+ size: cachedSize
2294
+ };
2295
+ onPreview({
2296
+ type: PdfAnnotationSubtype.STAMP,
2297
+ bounds: rect,
2298
+ data: { rect, ghostUrl: tool.defaults.imageSrc, pageRotation }
2299
+ });
2300
+ },
2209
2301
  onPointerDown: (pos) => {
2210
2302
  const tool = getTool();
2211
2303
  if (!tool) return;
2212
2304
  const { imageSrc, imageSize } = tool.defaults;
2213
- const placeStamp = (imageData, width, height) => {
2214
- var _a;
2215
- const rect = {
2216
- origin: { x: pos.x - width / 2, y: pos.y - height / 2 },
2217
- size: { width, height }
2218
- };
2219
- let anno = {
2220
- ...tool.defaults,
2221
- rect,
2222
- type: PdfAnnotationSubtype.STAMP,
2223
- name: tool.defaults.name ?? PdfAnnotationName.Image,
2224
- subject: tool.defaults.subject ?? "Stamp",
2225
- flags: tool.defaults.flags ?? ["print"],
2226
- pageIndex: context.pageIndex,
2227
- id: uuidV4(),
2228
- created: /* @__PURE__ */ new Date()
2229
- };
2230
- if ((_a = tool.behavior) == null ? void 0 : _a.insertUpright) {
2231
- anno = applyInsertUpright(anno, pageRotation, false);
2232
- }
2233
- anno = clampAnnotationToPage(anno, pageSize);
2234
- onCommit(anno, { imageData });
2235
- };
2236
2305
  if (imageSrc) {
2237
- services.processImage({
2238
- source: imageSrc,
2239
- maxWidth: pageSize.width,
2240
- maxHeight: pageSize.height,
2241
- onComplete: (result) => placeStamp(
2242
- result.imageData,
2243
- (imageSize == null ? void 0 : imageSize.width) ?? result.width,
2244
- (imageSize == null ? void 0 : imageSize.height) ?? result.height
2245
- )
2246
- });
2306
+ onPreview(null);
2307
+ if (cachedBuffer) {
2308
+ commitFromBuffer(pos, cachedBuffer, imageSize);
2309
+ } else {
2310
+ fetch(imageSrc).then((res) => res.arrayBuffer()).then((buffer) => commitFromBuffer(pos, buffer, imageSize));
2311
+ }
2247
2312
  } else {
2248
2313
  services.requestFile({
2249
2314
  accept: "image/png,image/jpeg",
2250
2315
  onFile: (file) => {
2251
- services.processImage({
2252
- source: file,
2253
- maxWidth: pageSize.width,
2254
- maxHeight: pageSize.height,
2255
- onComplete: (result) => placeStamp(result.imageData, result.width, result.height)
2256
- });
2316
+ file.arrayBuffer().then((buffer) => commitFromBuffer(pos, buffer));
2257
2317
  }
2258
2318
  });
2259
2319
  }
2320
+ },
2321
+ onPointerLeave: () => {
2322
+ onPreview(null);
2260
2323
  }
2261
2324
  };
2262
2325
  }
@@ -4302,12 +4365,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4302
4365
  setSelection: (ids) => this.setSelectionMethod(ids),
4303
4366
  deselectAnnotation: () => this.deselectAnnotation(),
4304
4367
  importAnnotations: (items) => this.importAnnotations(items),
4368
+ exportAnnotations: (options, documentId) => this.exportAnnotationsMethod(options, documentId),
4305
4369
  createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx),
4306
4370
  updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch),
4307
4371
  updateAnnotations: (patches) => this.updateAnnotationsMethod(patches),
4308
4372
  moveAnnotation: (pageIndex, id, position, mode, documentId) => this.moveAnnotationMethod(pageIndex, id, position, mode, documentId),
4309
4373
  deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id),
4310
4374
  deleteAnnotations: (annotations, documentId) => this.deleteAnnotationsMethod(annotations, documentId),
4375
+ deleteAllAnnotations: (documentId) => this.deleteAllAnnotationsMethod(documentId),
4311
4376
  purgeAnnotation: (pageIndex, id, documentId) => this.purgeAnnotationMethod(pageIndex, id, documentId),
4312
4377
  renderAnnotation: (options) => this.renderAnnotation(options),
4313
4378
  getPageAppearances: (pageIndex, options, documentId) => this.getPageAppearances(pageIndex, options, documentId),
@@ -4375,12 +4440,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4375
4440
  setActiveTool: (toolId, context) => this.setActiveTool(toolId, documentId, context),
4376
4441
  findToolForAnnotation: (anno) => this.findToolForAnnotation(anno),
4377
4442
  importAnnotations: (items) => this.importAnnotations(items, documentId),
4443
+ exportAnnotations: (options) => this.exportAnnotationsMethod(options, documentId),
4378
4444
  createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx, documentId),
4379
4445
  updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch, documentId),
4380
4446
  updateAnnotations: (patches) => this.updateAnnotationsMethod(patches, documentId),
4381
4447
  moveAnnotation: (pageIndex, id, position, mode) => this.moveAnnotationMethod(pageIndex, id, position, mode, documentId),
4382
4448
  deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id, documentId),
4383
4449
  deleteAnnotations: (annotations) => this.deleteAnnotationsMethod(annotations, documentId),
4450
+ deleteAllAnnotations: () => this.deleteAllAnnotationsMethod(documentId),
4384
4451
  purgeAnnotation: (pageIndex, id) => this.purgeAnnotationMethod(pageIndex, id, documentId),
4385
4452
  renderAnnotation: (options) => this.renderAnnotation(options, documentId),
4386
4453
  getPageAppearances: (pageIndex, options) => this.getPageAppearances(pageIndex, options, documentId),
@@ -4614,6 +4681,74 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4614
4681
  if (!pageMap) return;
4615
4682
  delete pageMap[annotId];
4616
4683
  }
4684
+ exportAnnotationsMethod(options, documentId) {
4685
+ const id = documentId ?? this.getActiveDocumentId();
4686
+ const docState = this.getDocumentState(id);
4687
+ const coreDoc = this.getCoreDocument(id);
4688
+ const doc = coreDoc == null ? void 0 : coreDoc.document;
4689
+ if (!doc) {
4690
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
4691
+ }
4692
+ const pageIndices = (options == null ? void 0 : options.pageIndex) !== void 0 ? [options.pageIndex] : Object.keys(docState.pages).map(Number);
4693
+ const entries = [];
4694
+ for (const pi of pageIndices) {
4695
+ const uids = docState.pages[pi] ?? [];
4696
+ for (const uid of uids) {
4697
+ const ta = docState.byUid[uid];
4698
+ if (!ta || ta.commitState === "deleted") continue;
4699
+ entries.push({
4700
+ annotation: ta.object,
4701
+ pageIndex: pi,
4702
+ isStamp: ta.object.type === PdfAnnotationSubtype.STAMP
4703
+ });
4704
+ }
4705
+ }
4706
+ const stampEntries = entries.filter((e) => e.isStamp);
4707
+ if (stampEntries.length === 0) {
4708
+ return PdfTaskHelper.resolve(entries.map((e) => ({ annotation: e.annotation })));
4709
+ }
4710
+ const resultTask = PdfTaskHelper.create();
4711
+ const appearances = /* @__PURE__ */ new Map();
4712
+ let pending = stampEntries.length;
4713
+ let failed = false;
4714
+ for (const entry of stampEntries) {
4715
+ const page = doc.pages.find((p) => p.index === entry.pageIndex);
4716
+ if (!page) {
4717
+ pending--;
4718
+ continue;
4719
+ }
4720
+ const exportTask = this.engine.exportAnnotationAppearanceAsPdf(doc, page, entry.annotation);
4721
+ exportTask.wait(
4722
+ (buffer) => {
4723
+ if (failed) return;
4724
+ appearances.set(entry.annotation.id, buffer);
4725
+ pending--;
4726
+ if (pending === 0) {
4727
+ resultTask.resolve(
4728
+ entries.map((e) => ({
4729
+ annotation: e.annotation,
4730
+ ...appearances.has(e.annotation.id) ? {
4731
+ ctx: {
4732
+ data: appearances.get(e.annotation.id),
4733
+ mimeType: "application/pdf"
4734
+ }
4735
+ } : {}
4736
+ }))
4737
+ );
4738
+ }
4739
+ },
4740
+ (error) => {
4741
+ if (failed) return;
4742
+ failed = true;
4743
+ resultTask.reject(error.reason);
4744
+ }
4745
+ );
4746
+ }
4747
+ if (pending === 0) {
4748
+ resultTask.resolve(entries.map((e) => ({ annotation: e.annotation })));
4749
+ }
4750
+ return resultTask;
4751
+ }
4617
4752
  importAnnotations(items, documentId) {
4618
4753
  const id = documentId ?? this.getActiveDocumentId();
4619
4754
  if (!this.isInitialLoadComplete.get(id)) {
@@ -4841,6 +4976,20 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
4841
4976
  this.deleteAnnotation(pageIndex, id, documentId);
4842
4977
  }
4843
4978
  }
4979
+ deleteAllAnnotationsMethod(documentId) {
4980
+ const docId = documentId ?? this.getActiveDocumentId();
4981
+ const docState = this.getDocumentState(docId);
4982
+ const toDelete = [];
4983
+ for (const [pageIdx, uids] of Object.entries(docState.pages)) {
4984
+ for (const uid of uids) {
4985
+ const ta = docState.byUid[uid];
4986
+ if (ta) toDelete.push({ pageIndex: Number(pageIdx), id: ta.object.id });
4987
+ }
4988
+ }
4989
+ if (toDelete.length > 0) {
4990
+ this.deleteAnnotationsMethod(toDelete, docId);
4991
+ }
4992
+ }
4844
4993
  purgeAnnotationMethod(pageIndex, id, documentId) {
4845
4994
  const docId = documentId ?? this.getActiveDocumentId();
4846
4995
  this.dispatch(purgeAnnotation(docId, pageIndex, id));