@embedpdf/plugin-stamp 3.0.0-next.12 → 3.0.0-next.13

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 CHANGED
@@ -1,10 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_contract = require("./contract-C6CgKD69.cjs");
2
+ const require_contract = require("./contract-T-v5KaYd.cjs");
3
3
  let _embedpdf_core = require("@embedpdf/core");
4
4
  let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
5
5
  let _embedpdf_core_acrojs = require("@embedpdf/core-acrojs");
6
6
  let _embedpdf_plugin_actions_contract_host = require("@embedpdf/plugin-actions/contract/host");
7
7
  let _embedpdf_plugin_annotation_contract = require("@embedpdf/plugin-annotation/contract");
8
+ let _embedpdf_plugin_annotation_contract_host = require("@embedpdf/plugin-annotation/contract/host");
8
9
  let _embedpdf_plugin_form_scripting = require("@embedpdf/plugin-form/scripting");
9
10
  //#region src/blank-library.ts
10
11
  /**
@@ -38,6 +39,51 @@ function blankLibraryPdf() {
38
39
  //#endregion
39
40
  //#region src/capability.ts
40
41
  const DEFAULT_PREVIEW_WIDTH = 256;
42
+ /** `#rrggbb` → the engine's sRGB triplet. */
43
+ const hexColor = (hex) => {
44
+ const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
45
+ if (!m) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `[stamp] mark color must be #rrggbb, got '${hex}'`);
46
+ const n = parseInt(m[1], 16);
47
+ return {
48
+ r: n >> 16 & 255,
49
+ g: n >> 8 & 255,
50
+ b: n & 255
51
+ };
52
+ };
53
+ /** An exact ArrayBuffer over a view (the engine's binary idiom). */
54
+ const toArrayBuffer = (bytes) => {
55
+ const out = new ArrayBuffer(bytes.byteLength);
56
+ new Uint8Array(out).set(bytes);
57
+ return out;
58
+ };
59
+ /** The rect an ink mark occupies, padded by its stroke. */
60
+ const inkBounds = (strokes, strokeWidth) => {
61
+ let left = Infinity;
62
+ let bottom = Infinity;
63
+ let right = -Infinity;
64
+ let top = -Infinity;
65
+ for (const stroke of strokes) for (const { x, y } of stroke) {
66
+ if (x < left) left = x;
67
+ if (x > right) right = x;
68
+ if (y < bottom) bottom = y;
69
+ if (y > top) top = y;
70
+ }
71
+ if (!Number.isFinite(left)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] an ink mark needs at least one point");
72
+ const pad = strokeWidth;
73
+ return {
74
+ left: left - pad,
75
+ bottom: bottom - pad,
76
+ right: right + pad,
77
+ top: top + pad
78
+ };
79
+ };
80
+ /** A generous box for typed text; the export crops to the glyphs. */
81
+ const textBounds = (text, fontSize) => ({
82
+ left: 0,
83
+ bottom: 0,
84
+ right: Math.max(1, text.length) * fontSize * .75 + fontSize,
85
+ top: fontSize * 1.6
86
+ });
41
87
  /** Session-unique ids. Assets are session-scoped for now (no persistence),
42
88
  * so a timestamp + counter is enough — durable ids come with the store port. */
43
89
  let seq = 0;
@@ -129,6 +175,7 @@ function createStampCapability(ctx, config = {}) {
129
175
  format: "png"
130
176
  }));
131
177
  const createLibrary = async (name, opts) => {
178
+ const kind = opts?.kind ?? "stamps";
132
179
  const taken = new Set(Object.keys(ctx.getState().libraries));
133
180
  const id = allocateId(opts?.id, "stamp-lib", taken);
134
181
  const doc = await openAssetDocument(blankLibraryPdf());
@@ -136,7 +183,10 @@ function createStampCapability(ctx, config = {}) {
136
183
  try {
137
184
  requireCanonicalServices(doc);
138
185
  await doc.metadata.update({ title: name });
139
- await doc.pieceInfo.update(require_contract.STAMP_LIBRARY_PIECEINFO_APP, require_contract.stampLibraryPieceInfo(id, { categories: opts?.categories }));
186
+ await doc.pieceInfo.update(require_contract.STAMP_LIBRARY_PIECEINFO_APP, require_contract.stampLibraryPieceInfo(id, {
187
+ kind,
188
+ categories: opts?.categories
189
+ }));
140
190
  bytes = await doc.download();
141
191
  } finally {
142
192
  await doc.close();
@@ -147,6 +197,7 @@ function createStampCapability(ctx, config = {}) {
147
197
  library: {
148
198
  id,
149
199
  name,
200
+ kind,
150
201
  assetIds: [],
151
202
  ...opts?.categories ? { categories: opts.categories } : {}
152
203
  }
@@ -180,6 +231,7 @@ function createStampCapability(ctx, config = {}) {
180
231
  if (!docMeta.title) await doc.metadata.update({ title: libraryName });
181
232
  const libraryCategories = opts?.categories ?? entryStringArray(catalogEntries, "Categories");
182
233
  const libraryLocale = entryString(catalogEntries, "Locale");
234
+ const libraryKind = opts?.libraryKind ?? require_contract.libraryKindFromPdfName(entryName(catalogEntries, "Kind"));
183
235
  const registry = (layout.namedPages ?? []).filter((entry) => entry.target.kind === "page");
184
236
  const hidden = (layout.namedPages ?? []).filter((entry) => entry.target.kind !== "page");
185
237
  if (hidden.length > 0) globalThis.console?.warn(`[stamp] library '${libraryName}' carries ${hidden.length} hidden-template or dangling registration(s); they are not stamps and were ignored`);
@@ -220,6 +272,7 @@ function createStampCapability(ctx, config = {}) {
220
272
  });
221
273
  }
222
274
  await doc.pieceInfo.update(require_contract.STAMP_LIBRARY_PIECEINFO_APP, require_contract.stampLibraryPieceInfo(libraryId, {
275
+ kind: libraryKind,
223
276
  categories: libraryCategories,
224
277
  locale: libraryLocale
225
278
  }));
@@ -261,6 +314,7 @@ function createStampCapability(ctx, config = {}) {
261
314
  library: {
262
315
  id: libraryId,
263
316
  name: libraryName,
317
+ kind: libraryKind,
264
318
  ...libraryLocale ? { locale: libraryLocale } : {},
265
319
  categories: libraryCategories,
266
320
  assetIds: []
@@ -292,9 +346,76 @@ function createStampCapability(ctx, config = {}) {
292
346
  });
293
347
  return imported.library.id;
294
348
  };
349
+ /**
350
+ * A drawn or typed mark rendered to a one-page PDF: the annotation is
351
+ * authored on a scratch page and exported the way "stamp from selection"
352
+ * exports — a fresh page the size of the mark, paths kept as paths, the
353
+ * font subset-embedded. Self-contained and Acrobat-readable.
354
+ */
355
+ const renderMark = async (mark) => {
356
+ const doc = await openAssetDocument(blankLibraryPdf());
357
+ try {
358
+ const scratch = (await doc.pages.list()).pages[0];
359
+ if (!scratch) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, "[stamp] no scratch page");
360
+ const page = doc.page(scratch.pageObjectNumber);
361
+ if (!page.annotations.exportAppearance) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] authoring a mark needs an asset engine that can export annotation appearances");
362
+ const color = hexColor(mark.color ?? "#1d2b53");
363
+ const created = mark.kind === "ink" ? await page.annotations.create({
364
+ subtype: "ink",
365
+ inkList: mark.strokes.map((stroke) => stroke.map(({ x, y }) => ({
366
+ x,
367
+ y
368
+ }))),
369
+ rect: inkBounds(mark.strokes, mark.strokeWidth ?? 2),
370
+ color,
371
+ strokeWidth: mark.strokeWidth ?? 2
372
+ }) : await page.annotations.create({
373
+ subtype: "free-text",
374
+ intent: "free-text",
375
+ contents: mark.text,
376
+ fontFamily: mark.fontFamily,
377
+ fontSize: mark.fontSize ?? 36,
378
+ textAlign: "left",
379
+ rect: textBounds(mark.text, mark.fontSize ?? 36),
380
+ fontColor: color,
381
+ strokeWidth: 0
382
+ });
383
+ return new Uint8Array(await page.annotations.exportAppearance([created.created.ref]));
384
+ } finally {
385
+ await doc.close();
386
+ }
387
+ };
388
+ /** One page of a PDF as a single-page PDF (a `pdf` mark with `pageIndex`). */
389
+ const extractPage = async (bytes, pageIndex) => {
390
+ const doc = await openAssetDocument(bytes);
391
+ try {
392
+ const layout = await doc.pages.list();
393
+ const page = layout.pages[pageIndex];
394
+ if (!page) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, `[stamp] the PDF has no page ${pageIndex} (${layout.pageCount} pages)`);
395
+ return await doc.pages.extract([page.pageObjectNumber]);
396
+ } finally {
397
+ await doc.close();
398
+ }
399
+ };
400
+ /** The bytes an asset is made from: the caller's `source`, or its `mark` authored into one. */
401
+ const resolveAssetSource = async (input) => {
402
+ if (input.source === void 0 === (input.mark === void 0)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] addAsset takes exactly one of `source` / `mark`");
403
+ if (input.source !== void 0) return (0, _embedpdf_engine_core_runtime.resolveBinarySource)(input.source);
404
+ const mark = input.mark;
405
+ switch (mark.kind) {
406
+ case "image": return (0, _embedpdf_engine_core_runtime.resolveBinarySource)(mark.source);
407
+ case "pdf": {
408
+ const resolved = await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(mark.source);
409
+ if (mark.pageIndex === void 0) return resolved;
410
+ return { bytes: toArrayBuffer(await extractPage(new Uint8Array(resolved.bytes), mark.pageIndex)) };
411
+ }
412
+ case "ink":
413
+ case "text": return { bytes: toArrayBuffer(await renderMark(mark)) };
414
+ }
415
+ };
295
416
  const addAsset = async (input) => {
296
417
  if (input.libraryId && !ctx.getState().libraries[input.libraryId]) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] unknown library '${input.libraryId}'`);
297
- const resolved = await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(input.source);
418
+ const resolved = await resolveAssetSource(input);
298
419
  const meta = (0, _embedpdf_engine_core_runtime.sniffBinaryMetadata)(resolved.bytes);
299
420
  if (!meta) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] asset source must be PNG, JPEG, or single-page PDF bytes");
300
421
  const isPdf = meta.mimeType === "application/pdf";
@@ -503,6 +624,43 @@ function createStampCapability(ctx, config = {}) {
503
624
  });
504
625
  });
505
626
  };
627
+ const updateLibrary = async (id, patch) => {
628
+ if (!ctx.getState().libraries[id]) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] unknown library '${id}'`);
629
+ return mutateLibrary(id, async () => {
630
+ const library = ctx.getState().libraries[id];
631
+ if (!library) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] unknown library '${id}'`);
632
+ const canonicalBytes = libraryBinaries.get(id);
633
+ if (!canonicalBytes) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `[stamp] canonical bytes are missing for library '${id}'`);
634
+ const next = {
635
+ ...library,
636
+ ...patch.name !== void 0 ? { name: patch.name } : {},
637
+ ...patch.categories !== void 0 ? { categories: patch.categories } : {}
638
+ };
639
+ const doc = await openAssetDocument(canonicalBytes);
640
+ let rewritten;
641
+ try {
642
+ requireCanonicalServices(doc);
643
+ if (next.name !== library.name) await doc.metadata.update({ title: next.name });
644
+ await doc.pieceInfo.update(require_contract.STAMP_LIBRARY_PIECEINFO_APP, require_contract.stampLibraryPieceInfo(id, {
645
+ kind: next.kind,
646
+ categories: next.categories,
647
+ locale: next.locale
648
+ }));
649
+ rewritten = await doc.download();
650
+ } finally {
651
+ await doc.close();
652
+ }
653
+ libraryBinaries.set(id, rewritten);
654
+ ctx.dispatch({
655
+ type: "LIBRARY_UPDATED",
656
+ library: next
657
+ });
658
+ libraryChanged.emit({
659
+ libraryId: id,
660
+ reason: "updated"
661
+ });
662
+ });
663
+ };
506
664
  /**
507
665
  * Form-backed stamps are executable templates only until placement. Work
508
666
  * on an isolated copy so neither the canonical library nor its reusable
@@ -582,6 +740,20 @@ function createStampCapability(ctx, config = {}) {
582
740
  ...stampPayload(asset, placement, placement !== bin),
583
741
  targetWidth: opts?.targetWidth
584
742
  });
743
+ armedByDocument.set(documentId, assetId);
744
+ };
745
+ /** Which asset this plugin armed per document. The annotation plugin owns
746
+ * the arm itself (a tool switch disarms it), so the answer is checked
747
+ * against it and forgotten once it no longer holds one. */
748
+ const armedByDocument = /* @__PURE__ */ new Map();
749
+ const armedAsset = (documentId) => {
750
+ const assetId = armedByDocument.get(documentId);
751
+ if (assetId === void 0) return null;
752
+ if (!ctx.tryForDocument(_embedpdf_plugin_annotation_contract_host.AnnotationToken, documentId)?.hasArmedStamp()) {
753
+ armedByDocument.delete(documentId);
754
+ return null;
755
+ }
756
+ return ctx.getState().assets[assetId] ?? null;
585
757
  };
586
758
  /**
587
759
  * Ghost renders, one per size bucket per asset, rendered lazily on the
@@ -652,9 +824,10 @@ function createStampCapability(ctx, config = {}) {
652
824
  if (ownedAssetEngine) Promise.resolve(ownedAssetEngine).then((engine) => engine.destroy()).catch(() => {});
653
825
  });
654
826
  return {
655
- libraries: () => {
827
+ libraries: (query) => {
656
828
  const s = ctx.getState();
657
- return s.libraryOrder.map((id) => s.libraries[id]).filter((l) => l != null);
829
+ const kinds = query?.kind === void 0 ? null : new Set(typeof query.kind === "string" ? [query.kind] : query.kind);
830
+ return s.libraryOrder.map((id) => s.libraries[id]).filter((l) => l != null && (kinds === null || kinds.has(l.kind)));
658
831
  },
659
832
  library: (id) => ctx.getState().libraries[id] ?? null,
660
833
  assets: (libraryId) => {
@@ -671,6 +844,7 @@ function createStampCapability(ctx, config = {}) {
671
844
  exportLibrary: (id) => libraryBinaries.get(id) ?? null,
672
845
  onLibraryChanged: libraryChanged.on,
673
846
  createLibrary,
847
+ updateLibrary,
674
848
  importLibraryPdf,
675
849
  addAsset,
676
850
  addAssetFromAnnotations,
@@ -679,7 +853,11 @@ function createStampCapability(ctx, config = {}) {
679
853
  removeLibrary,
680
854
  armAsset,
681
855
  placeAsset,
682
- disarm: (documentId) => ctx.forDocument(_embedpdf_plugin_annotation_contract.AnnotationToken, documentId).disarmStamp()
856
+ disarm: (documentId) => {
857
+ armedByDocument.delete(documentId);
858
+ ctx.forDocument(_embedpdf_plugin_annotation_contract.AnnotationToken, documentId).disarmStamp();
859
+ },
860
+ armedAsset
683
861
  };
684
862
  }
685
863
  //#endregion
@@ -701,6 +879,13 @@ const stampReducer = (state, action) => {
701
879
  },
702
880
  libraryOrder: [...state.libraryOrder, action.library.id]
703
881
  };
882
+ case "LIBRARY_UPDATED": return state.libraries[action.library.id] ? {
883
+ ...state,
884
+ libraries: {
885
+ ...state.libraries,
886
+ [action.library.id]: action.library
887
+ }
888
+ } : state;
704
889
  case "LIBRARY_REMOVED": {
705
890
  const library = state.libraries[action.libraryId];
706
891
  if (!library) return state;
@@ -851,12 +1036,15 @@ function persistStampLibraries(stamp, store, opts = {}) {
851
1036
  };
852
1037
  }
853
1038
  //#endregion
1039
+ exports.DEFAULT_LIBRARY_KIND = require_contract.DEFAULT_LIBRARY_KIND;
854
1040
  exports.STAMP_LIBRARY_PIECEINFO_APP = require_contract.STAMP_LIBRARY_PIECEINFO_APP;
855
1041
  exports.STAMP_PIECEINFO_APP = require_contract.STAMP_PIECEINFO_APP;
856
1042
  exports.STAMP_PIECEINFO_VERSION = require_contract.STAMP_PIECEINFO_VERSION;
857
1043
  exports.StampToken = require_contract.StampToken;
858
1044
  exports.assetIdFor = require_contract.assetIdFor;
859
1045
  exports.customStampName = require_contract.customStampName;
1046
+ exports.libraryKindFromPdfName = require_contract.libraryKindFromPdfName;
1047
+ exports.libraryKindToPdfName = require_contract.libraryKindToPdfName;
860
1048
  exports.memoryStampStore = memoryStampStore;
861
1049
  exports.parseStampKey = require_contract.parseStampKey;
862
1050
  exports.persistStampLibraries = persistStampLibraries;