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