@embedpdf/plugin-stamp 2.14.4 → 3.0.0-next.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/LICENSE +192 -21
- package/dist/index.cjs +591 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +187 -0
- package/dist/index.d.ts +187 -1
- package/dist/index.js +584 -975
- package/dist/index.js.map +1 -1
- package/package.json +23 -61
- package/dist/lib/actions.d.ts +0 -14
- package/dist/lib/defaults/name-map.d.ts +0 -2
- package/dist/lib/handlers/rubber-stamp.handler.d.ts +0 -4
- package/dist/lib/index.d.ts +0 -11
- package/dist/lib/manifest.d.ts +0 -4
- package/dist/lib/reducer.d.ts +0 -5
- package/dist/lib/stamp-plugin.d.ts +0 -54
- package/dist/lib/tools.d.ts +0 -58
- package/dist/lib/types.d.ts +0 -130
- package/dist/preact/adapter.d.ts +0 -5
- package/dist/preact/annotation.d.ts +0 -1
- package/dist/preact/core.d.ts +0 -1
- package/dist/preact/index.cjs +0 -2
- package/dist/preact/index.cjs.map +0 -1
- package/dist/preact/index.d.ts +0 -1
- package/dist/preact/index.js +0 -116
- package/dist/preact/index.js.map +0 -1
- package/dist/react/adapter.d.ts +0 -2
- package/dist/react/annotation.d.ts +0 -1
- package/dist/react/core.d.ts +0 -1
- package/dist/react/index.cjs +0 -2
- package/dist/react/index.cjs.map +0 -1
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.js +0 -115
- package/dist/react/index.js.map +0 -1
- package/dist/shared/components/index.d.ts +0 -1
- package/dist/shared/components/stamp-img.d.ts +0 -10
- package/dist/shared/hooks/index.d.ts +0 -1
- package/dist/shared/hooks/use-stamp.d.ts +0 -18
- package/dist/shared/index.d.ts +0 -4
- package/dist/shared-preact/components/index.d.ts +0 -1
- package/dist/shared-preact/components/stamp-img.d.ts +0 -10
- package/dist/shared-preact/hooks/index.d.ts +0 -1
- package/dist/shared-preact/hooks/use-stamp.d.ts +0 -18
- package/dist/shared-preact/index.d.ts +0 -4
- package/dist/shared-react/components/index.d.ts +0 -1
- package/dist/shared-react/components/stamp-img.d.ts +0 -10
- package/dist/shared-react/hooks/index.d.ts +0 -1
- package/dist/shared-react/hooks/use-stamp.d.ts +0 -18
- package/dist/shared-react/index.d.ts +0 -4
- package/dist/svelte/components/StampImg.svelte.d.ts +0 -10
- package/dist/svelte/components/index.d.ts +0 -1
- package/dist/svelte/hooks/index.d.ts +0 -1
- package/dist/svelte/hooks/use-stamp.svelte.d.ts +0 -23
- package/dist/svelte/index.cjs +0 -2
- package/dist/svelte/index.cjs.map +0 -1
- package/dist/svelte/index.d.ts +0 -4
- package/dist/svelte/index.js +0 -182
- package/dist/svelte/index.js.map +0 -1
- package/dist/vue/components/index.d.ts +0 -1
- package/dist/vue/components/stamp-img.vue.d.ts +0 -9
- package/dist/vue/hooks/index.d.ts +0 -1
- package/dist/vue/hooks/use-stamp.d.ts +0 -460
- package/dist/vue/index.cjs +0 -2
- package/dist/vue/index.cjs.map +0 -1
- package/dist/vue/index.d.ts +0 -4
- package/dist/vue/index.js +0 -189
- package/dist/vue/index.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,591 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("@embedpdf/core"),t=require("@embedpdf/models"),a=require("@embedpdf/plugin-annotation"),r="stamp",o={id:r,name:"Stamp Plugin",version:"1.0.0",provides:["stamp"],requires:["annotation"],optional:["i18n"],defaultConfig:{defaultLibrary:{id:"custom",name:"Custom Stamps",nameKey:"stamp.library.custom",categories:["custom","sidebar"]},manifests:[{url:"https://cdn.jsdelivr.net/npm/@embedpdf/default-stamps/{locale}/manifest.json",fallbackLocale:"en"}]}},n="STAMP/ADD_LIBRARY",i="STAMP/REMOVE_LIBRARY";function s(e){return{type:n,payload:e}}function l(e){return{type:i,payload:e}}const d={annotationType:t.PdfAnnotationSubtype.STAMP,create(e){const{onPreview:r,onCommit:o,getTool:n,pageSize:i,pageRotation:s,getToolContext:l}=e;return{onPointerMove:e=>{const a=l();if(!(null==a?void 0:a.stampSize))return;const{width:o,height:n}=a.stampSize;r({type:t.PdfAnnotationSubtype.STAMP,bounds:{origin:{x:e.x-o/2,y:e.y-n/2},size:{width:o,height:n}},data:{rect:{origin:{x:e.x-o/2,y:e.y-n/2},size:{width:o,height:n}},ghostUrl:a.ghostUrl,pageRotation:s}})},onPointerDown:d=>{var c;const m=n(),u=l();if(!m||!(null==u?void 0:u.stampSize))return;const{width:p,height:g}=u.stampSize,h={origin:{x:d.x-p/2,y:d.y-g/2},size:{width:p,height:g}};let f={...m.defaults,rect:h,type:t.PdfAnnotationSubtype.STAMP,name:u.stamp.name??m.defaults.name??t.PdfAnnotationName.Custom,subject:u.stamp.subject??m.defaults.subject??"Custom Stamp",flags:m.defaults.flags??["print"],pageIndex:e.pageIndex,id:t.uuidV4(),created:new Date};(null==(c=m.behavior)?void 0:c.insertUpright)&&(f=a.applyInsertUpright(f,s,!1)),f=a.clampAnnotationToPage(f,i),o(f,{appearance:u.appearance}),r(null)},onPointerLeave:()=>{r(null)}}}},c="rubberStamp",m=[a.defineAnnotationTool({id:c,name:"Rubber Stamp",labelKey:"stamp.rubberStamp",categories:["annotation","stamp","insert"],matchScore:()=>0,interaction:{exclusive:!0,cursor:"copy",isDraggable:!0,isResizable:!0,isRotatable:!1,lockAspectRatio:!0,isGroupDraggable:!0,isGroupResizable:!1,isGroupRotatable:!1},defaults:{type:t.PdfAnnotationSubtype.STAMP},behavior:{deactivateToolAfterCreate:!0,selectAfterCreate:!0,insertUpright:!0,useAppearanceStream:!0},pointerHandler:d})],u={Approved:t.PdfAnnotationName.Approved,Experimental:t.PdfAnnotationName.Experimental,NotApproved:t.PdfAnnotationName.NotApproved,AsIs:t.PdfAnnotationName.AsIs,Expired:t.PdfAnnotationName.Expired,NotForPublicRelease:t.PdfAnnotationName.NotForPublicRelease,Confidential:t.PdfAnnotationName.Confidential,Final:t.PdfAnnotationName.Final,Sold:t.PdfAnnotationName.Sold,Departmental:t.PdfAnnotationName.Departmental,ForComment:t.PdfAnnotationName.ForComment,TopSecret:t.PdfAnnotationName.TopSecret,Draft:t.PdfAnnotationName.Draft,ForPublicRelease:t.PdfAnnotationName.ForPublicRelease,Completed:t.PdfAnnotationName.Completed,Void:t.PdfAnnotationName.Void,PreliminaryResults:t.PdfAnnotationName.PreliminaryResults,InformationOnly:t.PdfAnnotationName.InformationOnly,Rejected:t.PdfAnnotationName.Rejected,Witness:t.PdfAnnotationName.Witness,InitialHere:t.PdfAnnotationName.InitialHere,SignHere:t.PdfAnnotationName.SignHere,Accepted:t.PdfAnnotationName.Accepted,Custom:t.PdfAnnotationName.Custom,Image:t.PdfAnnotationName.Image};const p=class extends e.BasePlugin{constructor(a,r,o){var n,i;if(super(a,r),this.config=o,this.instanceId=t.uuidV4(),this.libraries=new Map,this.libraryChange$=e.createEmitter(),this.activeStamp$=e.createScopedEmitter((e,t)=>({documentId:e,activeStamp:t})),this.managedManifests=[],this.annotation=null,this.i18n=null,this.localeUnsubscribe=null,this.toolChangeUnsubscribe=null,this.currentGhostUrl=null,this.annotation=(null==(n=r.getPlugin("annotation"))?void 0:n.provides())??null,this.annotation){for(const e of m)this.annotation.addTool(e);this.toolChangeUnsubscribe=this.annotation.onActiveToolChange(({documentId:e,tool:t})=>{(null==t?void 0:t.id)!==c&&(this.currentGhostUrl&&(URL.revokeObjectURL(this.currentGhostUrl),this.currentGhostUrl=null),this.activeStamp$.emit(e,null))})}this.i18n=(null==(i=r.getPlugin("i18n"))?void 0:i.provides())??null}async initialize(){if(this.config.libraries)for(const e of this.config.libraries)await this.loadLibraryInternal(e).toPromise();this.config.manifests&&this.config.manifests.length>0&&await this.initializeManifests()}buildCapability(){return{getLibraries:()=>this.getLibraries(),getStampsByCategory:e=>this.getStampsByCategory(e),renderStamp:(e,t,a,r)=>this.renderStamp(e,t,a,r),loadLibrary:e=>this.loadLibrary(e),loadLibraryFromManifest:e=>this.loadLibraryFromManifest(e),createNewLibrary:(e,t)=>this.createNewLibrary(e,t),addStampToLibrary:(e,t,a)=>this.addStampToLibrary(e,t,a),removeStampFromLibrary:(e,t)=>this.removeStampFromLibrary(e,t),updateStamp:(e,t,a)=>this.updateStamp(e,t,a),updateLibrary:(e,t)=>this.updateLibrary(e,t),removeLibrary:e=>this.removeLibrary(e),exportLibrary:e=>this.exportLibrary(e),forDocument:e=>this.createStampScope(e),onActiveStampChange:this.activeStamp$.onGlobal,onLibraryChange:this.libraryChange$.on}}getLibraries(){return Array.from(this.libraries.values())}getStampsByCategory(e){var t,a;const r=[];for(const o of this.libraries.values()){const n=(null==(t=o.categories)?void 0:t.includes(e))??!1;for(const t of o.stamps){const i=(null==(a=t.categories)?void 0:a.includes(e))??!1;(n||i)&&r.push({library:o,stamp:t})}}return r}renderStamp(e,a,r,o){const n=this.libraries.get(e);if(!n){const a=new t.Task;return a.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${e}`}),a}const i=n.document.pages[a];if(!i){const r=new t.Task;return r.reject({code:t.PdfErrorCode.NotFound,message:`Page ${a} not found in stamp library: ${e}`}),r}const s=r/i.size.width;return this.engine.renderPageRect(n.document,i,{origin:{x:0,y:0},size:i.size},{scaleFactor:s,dpr:o??1,withAnnotations:!0,rotation:i.rotation,transparentBackground:!0})}loadLibrary(e){return this.loadLibraryInternal(e)}createNewLibrary(e,a){const r=new t.Task,o=(null==a?void 0:a.id)??this.generateLibraryId(),n=this.getLibraryDocumentId(o);return this.engine.createDocument(n).wait(t=>{const n={id:o,name:e,nameKey:null==a?void 0:a.nameKey,document:t,stamps:[],categories:null==a?void 0:a.categories,readonly:(null==a?void 0:a.readonly)??!1};this.libraries.set(o,n),this.dispatch(s(o)),this.emitLibraryChange(),r.resolve(o)},t=>{this.logger.error("StampPlugin","CreateNewLibrary",`Failed to create library: ${e}`,t),r.fail(t)}),r}addStampToLibrary(e,a,r){const o=new t.Task,n=this.libraries.get(e);if(!n)return o.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${e}`}),o;if(n.readonly)return o.reject({code:t.PdfErrorCode.NotSupport,message:`Cannot add stamps to readonly library: ${e}`}),o;const i=`stamp-temp-${Date.now()}`;return this.engine.openDocumentBuffer({id:i,content:r}).wait(e=>{this.engine.importPages(n.document,e,[0]).wait(r=>{const i=r[0];n.document.pages.push(i),n.document.pageCount=n.document.pages.length;const s={...a,id:t.uuidV4(),pageIndex:i.index};n.stamps.push(s),this.engine.closeDocument(e).wait(()=>{this.emitLibraryChange(),o.resolve()},()=>{this.emitLibraryChange(),o.resolve()})},t=>{this.logger.error("StampPlugin","AddStampToLibrary","Failed to import page",t),this.engine.closeDocument(e).wait(()=>o.fail(t),()=>o.fail(t))})},e=>{this.logger.error("StampPlugin","AddStampToLibrary","Failed to open temp document",e),o.fail(e)}),o}createStampScope(e){return{createStampFromAnnotation:(t,a,r)=>this.createStampFromAnnotation(e,t,a,r),createStampFromAnnotations:(t,a,r)=>this.createStampFromAnnotations(e,t,a,r),activateStampPlacement:(t,a)=>this.activateStampPlacement(e,t,a),activateStampPlacementById:(t,a)=>this.activateStampPlacementById(e,t,a),getActiveStamp:()=>this.getActiveStamp(e),onActiveStampChange:this.activeStamp$.forScope(e)}}activateStampPlacementById(e,a,r){const o=new t.Task,n=this.libraries.get(a);if(!n)return o.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${a}`}),o;const i=n.stamps.find(e=>e.id===r);return i?this.activateStampPlacement(e,a,i):(o.reject({code:t.PdfErrorCode.NotFound,message:`Stamp ${r} not found in library: ${a}`}),o)}getActiveStamp(e){return this.activeStamp$.getValue(e)??null}activateStampPlacement(e,a,r){const o=new t.Task,n=this.libraries.get(a);if(!n)return o.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${a}`}),o;const i=n.document.pages[r.pageIndex];if(!i)return o.reject({code:t.PdfErrorCode.NotFound,message:`Page ${r.pageIndex} not found in library`}),o;const s=i.size;return this.engine.extractPages(n.document,[r.pageIndex]).wait(t=>{this.renderStamp(a,r.pageIndex,s.width,2).wait(n=>{var i;this.currentGhostUrl&&URL.revokeObjectURL(this.currentGhostUrl);const l=URL.createObjectURL(n);this.currentGhostUrl=l,null==(i=this.annotation)||i.setActiveTool(c,{appearance:t,ghostUrl:l,stampSize:s,libraryId:a,stamp:r}),this.activeStamp$.emit(e,{libraryId:a,stamp:r}),o.resolve()},e=>{this.logger.error("StampPlugin","ActivateStampPlacement","Failed to render stamp preview",e),o.fail(e)})},e=>{this.logger.error("StampPlugin","ActivateStampPlacement","Failed to extract stamp page",e),o.fail(e)}),o}createStampFromAnnotation(e,a,r,o){const n=new t.Task,i=this.getCoreDocument(e);if(!(null==i?void 0:i.document))return n.reject({code:t.PdfErrorCode.DocNotOpen,message:"document is not open"}),n;const s=i.document,l=s.pages[a.pageIndex];return l?(this.resolveTargetLibrary(o).wait(e=>{this.engine.exportAnnotationAppearanceAsPdf(s,l,a).wait(t=>{this.addStampToLibrary(e,r,t).wait(()=>n.resolve(),e=>n.fail(e))},e=>{this.logger.error("StampPlugin","CreateStampFromAnnotation","Failed to export annotation",e),n.fail(e)})},e=>n.fail(e)),n):(n.reject({code:t.PdfErrorCode.NotFound,message:`page ${a.pageIndex} not found`}),n)}createStampFromAnnotations(e,a,r,o){const n=new t.Task;if(0===a.length)return n.reject({code:t.PdfErrorCode.NotFound,message:"no annotations provided"}),n;const i=this.getCoreDocument(e);if(!(null==i?void 0:i.document))return n.reject({code:t.PdfErrorCode.DocNotOpen,message:"document is not open"}),n;const s=i.document,l=s.pages[a[0].pageIndex];return l?(this.resolveTargetLibrary(o).wait(e=>{this.engine.exportAnnotationsAppearanceAsPdf(s,l,a).wait(t=>{this.addStampToLibrary(e,r,t).wait(()=>n.resolve(),e=>n.fail(e))},e=>{this.logger.error("StampPlugin","CreateStampFromAnnotations","Failed to export annotations",e),n.fail(e)})},e=>n.fail(e)),n):(n.reject({code:t.PdfErrorCode.NotFound,message:`page ${a[0].pageIndex} not found`}),n)}resolveTargetLibrary(e){if(e&&this.libraries.has(e)){const a=new t.Task;return a.resolve(e),a}const a=this.config.defaultLibrary;if(!1===this.config.defaultLibrary||!a){const e=new t.Task;return e.reject({code:t.PdfErrorCode.NotSupport,message:"Default library creation is disabled"}),e}const r=a.id??"custom";if(this.libraries.has(r)){const e=new t.Task;return e.resolve(r),e}return this.createNewLibrary(a.name??"Custom Stamps",{id:r,nameKey:a.nameKey,categories:a.categories})}removeLibrary(e){const a=new t.Task,r=this.libraries.get(e);return r?(this.engine.closeDocument(r.document).wait(()=>{this.libraries.delete(e),this.dispatch(l(e)),this.emitLibraryChange(),a.resolve()},()=>{this.logger.warn("StampPlugin","RemoveLibrary",`Failed to close document for library: ${e}`),this.libraries.delete(e),this.dispatch(l(e)),this.emitLibraryChange(),a.resolve()}),a):(a.resolve(),a)}exportLibrary(e){const a=new t.Task,r=this.libraries.get(e);return r?(this.engine.saveAsCopy(r.document).wait(e=>{a.resolve({name:r.name,pdf:e,stamps:r.stamps,categories:r.categories})},t=>{this.logger.error("StampPlugin","ExportLibrary",`Failed to export library: ${e}`,t),a.fail(t)}),a):(a.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${e}`}),a)}loadLibraryInternal(e){const a=new t.Task,r=e.id??this.generateLibraryId(),o=this.getLibraryDocumentId(r);return("string"==typeof e.pdf?this.engine.openDocumentUrl({id:o,url:e.pdf}):this.engine.openDocumentBuffer({id:o,content:e.pdf})).wait(o=>{const n=e.stamps.map(e=>e.id?e:{...e,id:t.uuidV4()}),i={id:r,name:e.name,nameKey:e.nameKey,document:o,stamps:n,categories:e.categories,readonly:e.readonly??!1};this.libraries.set(r,i),this.dispatch(s(r)),this.emitLibraryChange(),a.resolve(r)},t=>{this.logger.error("StampPlugin","LoadLibrary",`Failed to load stamp library: ${e.name}`,t),a.fail(t)}),a}removeStampFromLibrary(e,a){const r=new t.Task,o=this.libraries.get(e);if(!o)return r.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${e}`}),r;if(o.readonly)return r.reject({code:t.PdfErrorCode.NotSupport,message:`Cannot remove stamps from readonly library: ${e}`}),r;const n=o.stamps.findIndex(e=>e.id===a);if(-1===n)return r.reject({code:t.PdfErrorCode.NotFound,message:`Stamp ${a} not found in library: ${e}`}),r;const i=o.stamps[n].pageIndex;return this.engine.deletePage(o.document,i).wait(()=>{o.stamps.splice(n,1),o.document.pages.splice(i,1),o.document.pageCount=o.document.pages.length;for(const e of o.stamps)e.pageIndex>i&&e.pageIndex--;for(let e=0;e<o.document.pages.length;e++)o.document.pages[e].index=e;this.emitLibraryChange(),r.resolve()},e=>{this.logger.error("StampPlugin","RemoveStampFromLibrary",`Failed to delete page for stamp ${a}`,e),r.fail(e)}),r}updateStamp(e,a,r){const o=new t.Task,n=this.libraries.get(e);if(!n)return o.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${e}`}),o;if(n.readonly)return o.reject({code:t.PdfErrorCode.NotSupport,message:`Cannot update stamps in readonly library: ${e}`}),o;const i=n.stamps.find(e=>e.id===a);return i?(Object.assign(i,r),this.emitLibraryChange(),o.resolve(),o):(o.reject({code:t.PdfErrorCode.NotFound,message:`Stamp ${a} not found in library: ${e}`}),o)}updateLibrary(e,a){const r=new t.Task,o=this.libraries.get(e);return o?o.readonly?(r.reject({code:t.PdfErrorCode.NotSupport,message:`Cannot update readonly library: ${e}`}),r):(void 0!==a.name&&(o.name=a.name),void 0!==a.nameKey&&(o.nameKey=a.nameKey),void 0!==a.categories&&(o.categories=a.categories),void 0!==a.readonly&&(o.readonly=a.readonly),this.emitLibraryChange(),r.resolve(),r):(r.reject({code:t.PdfErrorCode.NotFound,message:`Stamp library not found: ${e}`}),r)}loadLibraryFromManifest(e){return this.loadManifestUrl(e,!0)}async initializeManifests(){var e;const t=(null==(e=this.i18n)?void 0:e.getLocale())??"en";for(const a of this.config.manifests){const e=a.url.includes("{locale}"),r=e?t:"",o=a.url.replace("{locale}",r||t),n={source:a,localeAware:e,currentLocale:e?t:"",libraryId:null};this.managedManifests.push(n);try{const e=await this.loadManifestUrl(o,!0).toPromise();n.libraryId=e}catch{if(e&&a.fallbackLocale&&a.fallbackLocale!==t){const e=a.url.replace("{locale}",a.fallbackLocale);try{const t=await this.loadManifestUrl(e,!0).toPromise();n.libraryId=t,n.currentLocale=a.fallbackLocale}catch{this.logger.warn("StampPlugin","InitManifests",`Failed to load manifest (including fallback): ${a.url}`)}}else this.logger.warn("StampPlugin","InitManifests",`Failed to load manifest: ${o}`)}}this.i18n&&(this.localeUnsubscribe=this.i18n.onLocaleChange(e=>{this.handleLocaleChange(e.currentLocale)}))}async handleLocaleChange(e){for(const t of this.managedManifests){if(!t.localeAware||t.currentLocale===e)continue;if(t.libraryId){try{await this.removeLibrary(t.libraryId).toPromise()}catch{}t.libraryId=null}const a=t.source.url.replace("{locale}",e);try{const r=await this.loadManifestUrl(a,!0).toPromise();t.libraryId=r,t.currentLocale=e}catch{const a=t.source.fallbackLocale??"en";if(a!==e){const r=t.source.url.replace("{locale}",a);try{const e=await this.loadManifestUrl(r,!0).toPromise();t.libraryId=e,t.currentLocale=a}catch{this.logger.warn("StampPlugin","LocaleChange",`Failed to load manifest for locale ${e} (including fallback): ${t.source.url}`)}}else this.logger.warn("StampPlugin","LocaleChange",`Failed to load manifest for locale ${e}: ${t.source.url}`)}}}loadManifestUrl(e,a){const r=new t.Task;return fetch(e).then(e=>{if(!e.ok)throw new Error(`HTTP ${e.status}: ${e.statusText}`);return e.json()}).then(o=>{const n=this.resolveManifestPdfUrl(e,o.pdf),i=o.stamps.map(e=>{return{id:e.id??t.uuidV4(),pageIndex:e.pageIndex,name:(a=e.name,a?u[a]??t.PdfAnnotationName.Custom:t.PdfAnnotationName.Custom),subject:e.subject,subjectKey:e.subjectKey,label:e.label,categories:e.categories};var a}),s={id:o.id,name:o.name,nameKey:o.nameKey,pdf:n,stamps:i,categories:o.categories,readonly:a};this.loadLibraryInternal(s).wait(e=>r.resolve(e),e=>r.fail(e))}).catch(a=>{this.logger.error("StampPlugin","LoadManifest",`Failed to fetch manifest: ${e}`,a),r.reject({code:t.PdfErrorCode.NotFound,message:`Failed to load stamp manifest: ${e}`})}),r}resolveManifestPdfUrl(e,t){if(t.startsWith("http://")||t.startsWith("https://")||t.startsWith("/"))return t;return e.substring(0,e.lastIndexOf("/")+1)+t}generateLibraryId(){return t.uuidV4()}getLibraryDocumentId(e){return`stamp-doc-${this.instanceId}-${e}`}emitLibraryChange(){this.libraryChange$.emit(this.getLibraries())}async destroy(){this.currentGhostUrl&&(URL.revokeObjectURL(this.currentGhostUrl),this.currentGhostUrl=null),this.toolChangeUnsubscribe&&(this.toolChangeUnsubscribe(),this.toolChangeUnsubscribe=null),this.localeUnsubscribe&&(this.localeUnsubscribe(),this.localeUnsubscribe=null),this.activeStamp$.clear();const e=Array.from(this.libraries.values());for(const t of e)try{await this.engine.closeDocument(t.document).toPromise()}catch{}this.libraries.clear(),this.managedManifests.length=0,super.destroy()}};p.id=r;let g=p;const h={libraryIds:[]},f=(e=h,t)=>{switch(t.type){case n:return{...e,libraryIds:[...e.libraryIds,t.payload]};case i:return{...e,libraryIds:e.libraryIds.filter(e=>e!==t.payload)};default:return e}},b={manifest:o,create:(e,t)=>new g(r,e,t),reducer:f,initialState:h};exports.ADD_STAMP_LIBRARY=n,exports.REMOVE_STAMP_LIBRARY=i,exports.RUBBER_STAMP_TOOL_ID=c,exports.STAMP_PLUGIN_ID=r,exports.StampPlugin=g,exports.StampPluginPackage=b,exports.addStampLibrary=s,exports.initialState=h,exports.manifest=o,exports.removeStampLibrary=l,exports.stampReducer=f;
|
|
2
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _embedpdf_core = require("@embedpdf/core");
|
|
3
|
+
let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
|
|
4
|
+
let _embedpdf_plugin_annotation = require("@embedpdf/plugin-annotation");
|
|
5
|
+
let _embedpdf_plugin_form = require("@embedpdf/plugin-form");
|
|
6
|
+
//#region src/capability.ts
|
|
7
|
+
const DEFAULT_PREVIEW_WIDTH = 256;
|
|
8
|
+
const LIBRARY_PIECEINFO_APP = "EMBD_StampLibrary";
|
|
9
|
+
const STAMP_PIECEINFO_APP = "EMBD_Stamp";
|
|
10
|
+
const STAMP_SCHEMA_VERSION = 1;
|
|
11
|
+
/** Session-unique ids. Assets are session-scoped for now (no persistence),
|
|
12
|
+
* so a timestamp + counter is enough — durable ids come with the store port. */
|
|
13
|
+
let seq = 0;
|
|
14
|
+
const uid = (prefix) => `${prefix}-${Date.now().toString(36)}-${(seq++).toString(36)}`;
|
|
15
|
+
const entryString = (entries, key) => {
|
|
16
|
+
const entry = entries[key];
|
|
17
|
+
return entry?.type === "string" && entry.value.length > 0 ? entry.value : void 0;
|
|
18
|
+
};
|
|
19
|
+
const entryName = (entries, key) => {
|
|
20
|
+
const entry = entries[key];
|
|
21
|
+
return entry?.type === "name" && entry.value.length > 0 ? entry.value : void 0;
|
|
22
|
+
};
|
|
23
|
+
const entryStringArray = (entries, key) => {
|
|
24
|
+
const entry = entries[key];
|
|
25
|
+
return entry?.type === "string-array" ? [...entry.value] : void 0;
|
|
26
|
+
};
|
|
27
|
+
const kindToPdfName = (kind) => kind === "signature" ? "Signature" : kind === "initials" ? "Initials" : "Stamp";
|
|
28
|
+
const kindFromPdfName = (name) => {
|
|
29
|
+
switch (name?.toLowerCase()) {
|
|
30
|
+
case "stamp": return "stamp";
|
|
31
|
+
case "signature": return "signature";
|
|
32
|
+
case "initials": return "initials";
|
|
33
|
+
default: return;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const metadataPatch = (id, name, kind, subject, categories) => ({
|
|
37
|
+
Version: STAMP_SCHEMA_VERSION,
|
|
38
|
+
Id: id,
|
|
39
|
+
Name: name,
|
|
40
|
+
Kind: { name: kindToPdfName(kind) },
|
|
41
|
+
Subject: subject ?? null,
|
|
42
|
+
Categories: categories ?? null
|
|
43
|
+
});
|
|
44
|
+
const libraryMetadataPatch = (id, name, categories) => ({
|
|
45
|
+
Version: STAMP_SCHEMA_VERSION,
|
|
46
|
+
Id: id,
|
|
47
|
+
Name: name,
|
|
48
|
+
Kind: { name: "StampLibrary" },
|
|
49
|
+
Categories: categories ?? null
|
|
50
|
+
});
|
|
51
|
+
function createStampCapability(ctx, config = {}) {
|
|
52
|
+
/** Binary sidecar of the serializable store: asset bytes + cached preview,
|
|
53
|
+
* keyed by asset id. The reducer never sees these (kernel rule 1). */
|
|
54
|
+
const binaries = /* @__PURE__ */ new Map();
|
|
55
|
+
/** The durable source of truth for a canonical library. Asset PDFs above
|
|
56
|
+
* are derived page extractions; this map owns the rewritten whole PDF. */
|
|
57
|
+
const libraryBinaries = /* @__PURE__ */ new Map();
|
|
58
|
+
/** Whole-PDF rewrites must be serialized per library or two concurrent
|
|
59
|
+
* appends could both start from the same bytes and lose one page. */
|
|
60
|
+
const libraryMutationTails = /* @__PURE__ */ new Map();
|
|
61
|
+
const mutateLibrary = (libraryId, mutation) => {
|
|
62
|
+
const result = (libraryMutationTails.get(libraryId) ?? Promise.resolve()).then(mutation);
|
|
63
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
64
|
+
libraryMutationTails.set(libraryId, tail);
|
|
65
|
+
return result.finally(() => {
|
|
66
|
+
if (libraryMutationTails.get(libraryId) === tail) libraryMutationTails.delete(libraryId);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
let assetEngineRef = null;
|
|
70
|
+
const assetEngine = () => {
|
|
71
|
+
if (!assetEngineRef) {
|
|
72
|
+
const cfg = config.assetEngine;
|
|
73
|
+
assetEngineRef = !cfg ? ctx.engine : typeof cfg === "function" ? cfg() : cfg;
|
|
74
|
+
}
|
|
75
|
+
return assetEngineRef;
|
|
76
|
+
};
|
|
77
|
+
const openAssetDocument = async (bytes, identity) => {
|
|
78
|
+
try {
|
|
79
|
+
return await (await assetEngine()).open({
|
|
80
|
+
kind: "bytes",
|
|
81
|
+
id: uid("stamp-import"),
|
|
82
|
+
bytes
|
|
83
|
+
}, {
|
|
84
|
+
scope: ["*"],
|
|
85
|
+
...identity ? { identity } : {}
|
|
86
|
+
});
|
|
87
|
+
} catch (err) {
|
|
88
|
+
if (!config.assetEngine && _embedpdf_engine_core_runtime.EngineError.is(err, _embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg)) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] importing a library PDF needs an engine that can open local bytes, and this viewer's engine cannot (cloud). Pass stampPlugin({ assetEngine: () => import('@embedpdf/engine').then((m) => m.createLocalEngine()) }) — it loads lazily, on first import.");
|
|
89
|
+
throw err;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
/** Import-time preview render → bytes. The asset engine is local by
|
|
93
|
+
* definition, so the image source is always inline bytes. */
|
|
94
|
+
const imageToPreview = (image) => {
|
|
95
|
+
if (image.source.kind !== "bytes") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] asset engine returned a URL-sourced render; asset engines must be local");
|
|
96
|
+
return {
|
|
97
|
+
bytes: image.source.bytes,
|
|
98
|
+
mimeType: image.contentType
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
const allocateId = (preferred, prefix, taken) => {
|
|
102
|
+
let id = preferred;
|
|
103
|
+
if (!id || taken.has(id)) do
|
|
104
|
+
id = uid(prefix);
|
|
105
|
+
while (taken.has(id));
|
|
106
|
+
taken.add(id);
|
|
107
|
+
return id;
|
|
108
|
+
};
|
|
109
|
+
const createLooseLibrary = (name) => {
|
|
110
|
+
const id = uid("stamp-lib");
|
|
111
|
+
ctx.dispatch({
|
|
112
|
+
type: "LIBRARY_ADDED",
|
|
113
|
+
library: {
|
|
114
|
+
id,
|
|
115
|
+
name,
|
|
116
|
+
storage: "loose",
|
|
117
|
+
assetIds: []
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return id;
|
|
121
|
+
};
|
|
122
|
+
const importLibraryPdf = async (source, opts) => {
|
|
123
|
+
const resolved = await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(source);
|
|
124
|
+
if ((0, _embedpdf_engine_core_runtime.sniffBinaryMetadata)(resolved.bytes)?.mimeType !== "application/pdf") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] importLibraryPdf needs PDF bytes (use addAsset for a raster image)");
|
|
125
|
+
const doc = await openAssetDocument(new Uint8Array(resolved.bytes));
|
|
126
|
+
let imported;
|
|
127
|
+
try {
|
|
128
|
+
if (!doc.pieceInfo || !doc.pages.extract) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] canonical PDF libraries need an asset engine with pieceInfo and pages.extract");
|
|
129
|
+
const snapshot = await doc.pages.list();
|
|
130
|
+
if (snapshot.pageCount === 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] a canonical stamp library PDF must contain at least one page");
|
|
131
|
+
const pages = snapshot.pages.map((layout) => {
|
|
132
|
+
const handle = doc.page(layout.pageObjectNumber);
|
|
133
|
+
if (!handle.pieceInfo) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] canonical PDF libraries need page pieceInfo support");
|
|
134
|
+
return {
|
|
135
|
+
layout,
|
|
136
|
+
handle,
|
|
137
|
+
metadata: null
|
|
138
|
+
};
|
|
139
|
+
});
|
|
140
|
+
const catalogMetadata = await doc.pieceInfo.read(LIBRARY_PIECEINFO_APP);
|
|
141
|
+
for (const page of pages) page.metadata = await page.handle.pieceInfo.read(STAMP_PIECEINFO_APP);
|
|
142
|
+
const state = ctx.getState();
|
|
143
|
+
const takenLibraryIds = new Set(Object.keys(state.libraries));
|
|
144
|
+
const takenAssetIds = new Set(Object.keys(state.assets));
|
|
145
|
+
const catalogEntries = catalogMetadata?.entries ?? {};
|
|
146
|
+
const libraryId = allocateId(entryString(catalogEntries, "Id"), "stamp-lib", takenLibraryIds);
|
|
147
|
+
const libraryName = opts?.name ?? entryString(catalogEntries, "Name") ?? "Stamps";
|
|
148
|
+
const libraryCategories = opts?.categories ?? entryStringArray(catalogEntries, "Categories");
|
|
149
|
+
const descriptors = pages.map(({ layout, handle, metadata: pageMetadata }) => {
|
|
150
|
+
const entries = pageMetadata?.entries ?? {};
|
|
151
|
+
const id = allocateId(entryString(entries, "Id"), "stamp", takenAssetIds);
|
|
152
|
+
const kind = opts?.kind ?? kindFromPdfName(entryName(entries, "Kind")) ?? "stamp";
|
|
153
|
+
return {
|
|
154
|
+
handle,
|
|
155
|
+
asset: {
|
|
156
|
+
id,
|
|
157
|
+
libraryId,
|
|
158
|
+
kind,
|
|
159
|
+
name: opts?.assetName?.(layout.index) ?? entryString(entries, "Name") ?? `Stamp ${layout.index + 1}`,
|
|
160
|
+
size: {
|
|
161
|
+
width: layout.size.width,
|
|
162
|
+
height: layout.size.height
|
|
163
|
+
},
|
|
164
|
+
format: "pdf",
|
|
165
|
+
pageObjectNumber: layout.pageObjectNumber,
|
|
166
|
+
subject: entryString(entries, "Subject"),
|
|
167
|
+
categories: entryStringArray(entries, "Categories")
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
});
|
|
171
|
+
await doc.pieceInfo.update(LIBRARY_PIECEINFO_APP, libraryMetadataPatch(libraryId, libraryName, libraryCategories));
|
|
172
|
+
const assets = [];
|
|
173
|
+
for (const descriptor of descriptors) {
|
|
174
|
+
const { asset, handle } = descriptor;
|
|
175
|
+
await handle.pieceInfo.update(STAMP_PIECEINFO_APP, metadataPatch(asset.id, asset.name, asset.kind, asset.subject, asset.categories));
|
|
176
|
+
const bytes = await doc.pages.extract([asset.pageObjectNumber]);
|
|
177
|
+
const image = await handle.render.image({
|
|
178
|
+
viewport: {
|
|
179
|
+
kind: "width",
|
|
180
|
+
width: config.previewWidth ?? DEFAULT_PREVIEW_WIDTH
|
|
181
|
+
},
|
|
182
|
+
background: "transparent",
|
|
183
|
+
includeAnnotations: true,
|
|
184
|
+
format: "png"
|
|
185
|
+
});
|
|
186
|
+
assets.push({
|
|
187
|
+
asset,
|
|
188
|
+
bytes,
|
|
189
|
+
preview: imageToPreview(image)
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
imported = {
|
|
193
|
+
library: {
|
|
194
|
+
id: libraryId,
|
|
195
|
+
name: libraryName,
|
|
196
|
+
storage: "canonical-pdf",
|
|
197
|
+
categories: libraryCategories,
|
|
198
|
+
assetIds: []
|
|
199
|
+
},
|
|
200
|
+
canonicalBytes: await doc.download(),
|
|
201
|
+
assets
|
|
202
|
+
};
|
|
203
|
+
} finally {
|
|
204
|
+
await doc.close();
|
|
205
|
+
}
|
|
206
|
+
libraryBinaries.set(imported.library.id, imported.canonicalBytes);
|
|
207
|
+
ctx.dispatch({
|
|
208
|
+
type: "LIBRARY_ADDED",
|
|
209
|
+
library: imported.library
|
|
210
|
+
});
|
|
211
|
+
for (const { asset, bytes, preview } of imported.assets) {
|
|
212
|
+
binaries.set(asset.id, {
|
|
213
|
+
bytes,
|
|
214
|
+
preview
|
|
215
|
+
});
|
|
216
|
+
ctx.dispatch({
|
|
217
|
+
type: "ASSET_ADDED",
|
|
218
|
+
asset
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
return imported.library.id;
|
|
222
|
+
};
|
|
223
|
+
const addAsset = async (input) => {
|
|
224
|
+
const targetLibrary = input.libraryId ? ctx.getState().libraries[input.libraryId] ?? null : null;
|
|
225
|
+
if (input.libraryId && !targetLibrary) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] unknown library '${input.libraryId}'`);
|
|
226
|
+
const resolved = await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(input.source);
|
|
227
|
+
const meta = (0, _embedpdf_engine_core_runtime.sniffBinaryMetadata)(resolved.bytes);
|
|
228
|
+
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");
|
|
229
|
+
const isPdf = meta.mimeType === "application/pdf";
|
|
230
|
+
let suppliedPreview = null;
|
|
231
|
+
if (input.preview) {
|
|
232
|
+
const preview = await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(input.preview);
|
|
233
|
+
suppliedPreview = {
|
|
234
|
+
bytes: new Uint8Array(preview.bytes),
|
|
235
|
+
mimeType: preview.mimeType ?? "image/png"
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (targetLibrary?.storage === "canonical-pdf") {
|
|
239
|
+
if (!isPdf) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] raster assets cannot be appended to a canonical PDF library");
|
|
240
|
+
return mutateLibrary(targetLibrary.id, async () => {
|
|
241
|
+
const liveLibrary = ctx.getState().libraries[targetLibrary.id];
|
|
242
|
+
if (liveLibrary?.storage !== "canonical-pdf") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] canonical library '${targetLibrary.id}' no longer exists`);
|
|
243
|
+
const canonicalBytes = libraryBinaries.get(liveLibrary.id);
|
|
244
|
+
if (!canonicalBytes) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `[stamp] canonical bytes are missing for library '${liveLibrary.id}'`);
|
|
245
|
+
const assetId = uid("stamp");
|
|
246
|
+
const doc = await openAssetDocument(canonicalBytes);
|
|
247
|
+
let appended;
|
|
248
|
+
try {
|
|
249
|
+
if (!doc.pages.insert || !doc.pages.extract) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] appending to a canonical library needs pages.insert and pages.extract");
|
|
250
|
+
const result = await doc.pages.insert(new Uint8Array(resolved.bytes));
|
|
251
|
+
if (result.insertedPageObjectNumbers.length !== 1) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] a canonical library asset must be a single-page PDF");
|
|
252
|
+
const pageObjectNumber = result.insertedPageObjectNumbers[0];
|
|
253
|
+
const layout = result.layout.pages.find((candidate) => candidate.pageObjectNumber === pageObjectNumber);
|
|
254
|
+
const page = doc.page(pageObjectNumber);
|
|
255
|
+
if (!layout || !page.pieceInfo) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] canonical PDF libraries need page layout and pieceInfo support");
|
|
256
|
+
const asset = {
|
|
257
|
+
id: assetId,
|
|
258
|
+
libraryId: liveLibrary.id,
|
|
259
|
+
kind: input.kind ?? "stamp",
|
|
260
|
+
name: input.name,
|
|
261
|
+
size: {
|
|
262
|
+
width: layout.size.width,
|
|
263
|
+
height: layout.size.height
|
|
264
|
+
},
|
|
265
|
+
format: "pdf",
|
|
266
|
+
pageObjectNumber,
|
|
267
|
+
subject: input.subject,
|
|
268
|
+
categories: input.categories
|
|
269
|
+
};
|
|
270
|
+
await page.pieceInfo.update(STAMP_PIECEINFO_APP, metadataPatch(asset.id, asset.name, asset.kind, asset.subject, asset.categories));
|
|
271
|
+
const bytes = await doc.pages.extract([pageObjectNumber]);
|
|
272
|
+
let preview = suppliedPreview;
|
|
273
|
+
if (!preview) preview = imageToPreview(await page.render.image({
|
|
274
|
+
viewport: {
|
|
275
|
+
kind: "width",
|
|
276
|
+
width: config.previewWidth ?? DEFAULT_PREVIEW_WIDTH
|
|
277
|
+
},
|
|
278
|
+
background: "transparent",
|
|
279
|
+
includeAnnotations: true,
|
|
280
|
+
format: "png"
|
|
281
|
+
}));
|
|
282
|
+
appended = {
|
|
283
|
+
asset,
|
|
284
|
+
bytes,
|
|
285
|
+
preview,
|
|
286
|
+
canonical: await doc.download()
|
|
287
|
+
};
|
|
288
|
+
} finally {
|
|
289
|
+
await doc.close();
|
|
290
|
+
}
|
|
291
|
+
libraryBinaries.set(liveLibrary.id, appended.canonical);
|
|
292
|
+
binaries.set(appended.asset.id, {
|
|
293
|
+
bytes: appended.bytes,
|
|
294
|
+
preview: appended.preview
|
|
295
|
+
});
|
|
296
|
+
ctx.dispatch({
|
|
297
|
+
type: "ASSET_ADDED",
|
|
298
|
+
asset: appended.asset
|
|
299
|
+
});
|
|
300
|
+
return appended.asset.id;
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
const size = input.size ?? ("width" in meta ? {
|
|
304
|
+
width: meta.width,
|
|
305
|
+
height: meta.height
|
|
306
|
+
} : null);
|
|
307
|
+
if (!size) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, "[stamp] a PDF asset needs `size` (its page size in points) — PDF bytes carry no sniffable dimensions");
|
|
308
|
+
let preview = suppliedPreview;
|
|
309
|
+
if (!preview && !isPdf) preview = {
|
|
310
|
+
bytes: new Uint8Array(resolved.bytes),
|
|
311
|
+
mimeType: meta.mimeType
|
|
312
|
+
};
|
|
313
|
+
const commitLooseAsset = (libraryId) => {
|
|
314
|
+
const asset = {
|
|
315
|
+
id: uid("stamp"),
|
|
316
|
+
libraryId,
|
|
317
|
+
kind: input.kind ?? "stamp",
|
|
318
|
+
name: input.name,
|
|
319
|
+
size,
|
|
320
|
+
format: isPdf ? "pdf" : meta.mimeType === "image/png" ? "png" : "jpeg",
|
|
321
|
+
subject: input.subject,
|
|
322
|
+
categories: input.categories
|
|
323
|
+
};
|
|
324
|
+
binaries.set(asset.id, {
|
|
325
|
+
bytes: new Uint8Array(resolved.bytes),
|
|
326
|
+
preview
|
|
327
|
+
});
|
|
328
|
+
ctx.dispatch({
|
|
329
|
+
type: "ASSET_ADDED",
|
|
330
|
+
asset
|
|
331
|
+
});
|
|
332
|
+
return asset.id;
|
|
333
|
+
};
|
|
334
|
+
if (!targetLibrary) return commitLooseAsset(createLooseLibrary(input.name));
|
|
335
|
+
return mutateLibrary(targetLibrary.id, async () => {
|
|
336
|
+
const liveLibrary = ctx.getState().libraries[targetLibrary.id];
|
|
337
|
+
if (liveLibrary?.storage !== "loose") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] loose library '${targetLibrary.id}' no longer exists`);
|
|
338
|
+
return commitLooseAsset(liveLibrary.id);
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
const dropLibrary = (id) => {
|
|
342
|
+
const library = ctx.getState().libraries[id];
|
|
343
|
+
if (library) for (const assetId of library.assetIds) binaries.delete(assetId);
|
|
344
|
+
libraryBinaries.delete(id);
|
|
345
|
+
ctx.dispatch({
|
|
346
|
+
type: "LIBRARY_REMOVED",
|
|
347
|
+
libraryId: id
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
const removeAsset = async (id) => {
|
|
351
|
+
const initialAsset = ctx.getState().assets[id];
|
|
352
|
+
if (!initialAsset) return;
|
|
353
|
+
return mutateLibrary(initialAsset.libraryId, async () => {
|
|
354
|
+
const asset = ctx.getState().assets[id];
|
|
355
|
+
if (!asset) return;
|
|
356
|
+
const library = ctx.getState().libraries[asset.libraryId];
|
|
357
|
+
if (library?.storage === "canonical-pdf") {
|
|
358
|
+
if (library.assetIds.length === 1) {
|
|
359
|
+
dropLibrary(library.id);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
if (asset.pageObjectNumber === void 0) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `[stamp] canonical asset '${id}' has no page object number`);
|
|
363
|
+
const canonicalBytes = libraryBinaries.get(library.id);
|
|
364
|
+
if (!canonicalBytes) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `[stamp] canonical bytes are missing for library '${library.id}'`);
|
|
365
|
+
const doc = await openAssetDocument(canonicalBytes);
|
|
366
|
+
let rewritten;
|
|
367
|
+
try {
|
|
368
|
+
await doc.pages.delete([asset.pageObjectNumber]);
|
|
369
|
+
rewritten = await doc.download();
|
|
370
|
+
} finally {
|
|
371
|
+
await doc.close();
|
|
372
|
+
}
|
|
373
|
+
libraryBinaries.set(library.id, rewritten);
|
|
374
|
+
}
|
|
375
|
+
binaries.delete(id);
|
|
376
|
+
ctx.dispatch({
|
|
377
|
+
type: "ASSET_REMOVED",
|
|
378
|
+
assetId: id
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
const removeLibrary = (id) => mutateLibrary(id, async () => dropLibrary(id));
|
|
383
|
+
const surfaceScriptingResult = (result) => {
|
|
384
|
+
try {
|
|
385
|
+
for (const effect of result.uiEffects) config.scripting?.onUiEffect?.(effect);
|
|
386
|
+
for (const diagnostic of result.diagnostics) config.scripting?.onDiagnostic?.(diagnostic);
|
|
387
|
+
if (result.error) config.scripting?.onError?.(result.error);
|
|
388
|
+
} catch (error) {
|
|
389
|
+
globalThis.console?.error("[stamp] scripting observer failed:", error);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
/**
|
|
393
|
+
* Form-backed stamps are executable templates only until placement. Work
|
|
394
|
+
* on an isolated copy so neither the canonical library nor its reusable
|
|
395
|
+
* per-page extraction becomes target/user/time specific.
|
|
396
|
+
*/
|
|
397
|
+
const materializeForPlacement = async (documentId, asset, bin) => {
|
|
398
|
+
if (asset.format !== "pdf" || !config.scripting?.enabled) return bin;
|
|
399
|
+
const targetDoc = ctx.documentHandle(documentId);
|
|
400
|
+
const targetMeta = ctx.core().documents[documentId] ?? null;
|
|
401
|
+
if (!targetDoc || !targetMeta) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] target document '${documentId}' is not open`);
|
|
402
|
+
const canonicalBytes = libraryBinaries.get(asset.libraryId);
|
|
403
|
+
const sourceBytes = canonicalBytes ?? bin.bytes;
|
|
404
|
+
const doc = await openAssetDocument(new Uint8Array(sourceBytes), targetDoc.security.identity);
|
|
405
|
+
let scripting = null;
|
|
406
|
+
try {
|
|
407
|
+
const layout = await doc.pages.list();
|
|
408
|
+
const selectedPage = asset.pageObjectNumber === void 0 ? layout.pageCount === 1 ? layout.pages[0] : void 0 : layout.pages.find(({ pageObjectNumber }) => pageObjectNumber === asset.pageObjectNumber);
|
|
409
|
+
if (!selectedPage) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.InvalidArg, canonicalBytes ? `[stamp] canonical page ${asset.pageObjectNumber ?? "unknown"} no longer exists` : "[stamp] a loose dynamic stamp asset must contain exactly one page");
|
|
410
|
+
const snapshot = await doc.forms.list();
|
|
411
|
+
if (!snapshot.fields.some((field) => field.widgets.some(({ pageObjectNumber }) => pageObjectNumber === selectedPage.pageObjectNumber))) return bin;
|
|
412
|
+
if (!doc.pages.flatten || !doc.pages.extract) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotImplemented, "[stamp] dynamic PDF stamps need an asset engine with pages.flatten and pages.extract");
|
|
413
|
+
const scriptingConfig = config.scripting;
|
|
414
|
+
scripting = (0, _embedpdf_plugin_form.createFormScriptingController)({
|
|
415
|
+
doc,
|
|
416
|
+
document: () => targetMeta,
|
|
417
|
+
config: scriptingConfig,
|
|
418
|
+
sandboxFactory: scriptingConfig.sandboxFactory ?? (() => import("@embedpdf/core-js-sandbox").then(({ createQuickJsSandbox }) => createQuickJsSandbox()))
|
|
419
|
+
});
|
|
420
|
+
const result = await scripting.recalculate(snapshot);
|
|
421
|
+
surfaceScriptingResult(result);
|
|
422
|
+
if (result.status === "failed") throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `[stamp] dynamic stamp scripting failed: ${result.error?.message ?? "native form effect failed"}`);
|
|
423
|
+
const pageObjectNumber = selectedPage.pageObjectNumber;
|
|
424
|
+
const failed = (await doc.pages.flatten([pageObjectNumber], "display")).results.find(({ status }) => status === "failed" || status === "skipped");
|
|
425
|
+
if (failed) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.Unknown, `[stamp] dynamic stamp flatten failed for page ${failed.pageObjectNumber}`);
|
|
426
|
+
const bytes = await doc.pages.extract([pageObjectNumber]);
|
|
427
|
+
const image = await doc.page(pageObjectNumber).render.image({
|
|
428
|
+
viewport: {
|
|
429
|
+
kind: "width",
|
|
430
|
+
width: config.previewWidth ?? DEFAULT_PREVIEW_WIDTH
|
|
431
|
+
},
|
|
432
|
+
background: "transparent",
|
|
433
|
+
includeAnnotations: false,
|
|
434
|
+
format: "png"
|
|
435
|
+
});
|
|
436
|
+
return {
|
|
437
|
+
bytes,
|
|
438
|
+
preview: imageToPreview(image)
|
|
439
|
+
};
|
|
440
|
+
} finally {
|
|
441
|
+
scripting?.dispose();
|
|
442
|
+
await doc.close();
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
const armAsset = async (documentId, assetId, opts) => {
|
|
446
|
+
const asset = ctx.getState().assets[assetId];
|
|
447
|
+
const bin = binaries.get(assetId);
|
|
448
|
+
if (!asset || !bin) throw new _embedpdf_engine_core_runtime.EngineError(_embedpdf_engine_core_runtime.EngineErrorCode.NotFound, `[stamp] unknown asset '${assetId}'`);
|
|
449
|
+
const placement = await materializeForPlacement(documentId, asset, bin);
|
|
450
|
+
await ctx.forDocument(_embedpdf_plugin_annotation.AnnotationToken, documentId).armStamp({
|
|
451
|
+
source: placement.bytes,
|
|
452
|
+
preview: placement.preview ? {
|
|
453
|
+
data: placement.preview.bytes,
|
|
454
|
+
mimeType: placement.preview.mimeType
|
|
455
|
+
} : void 0,
|
|
456
|
+
intrinsicSize: asset.size,
|
|
457
|
+
targetWidth: opts?.targetWidth
|
|
458
|
+
});
|
|
459
|
+
};
|
|
460
|
+
ctx.cleanup(() => {
|
|
461
|
+
binaries.clear();
|
|
462
|
+
libraryBinaries.clear();
|
|
463
|
+
libraryMutationTails.clear();
|
|
464
|
+
const ownedAssetEngine = typeof config.assetEngine === "function" ? assetEngineRef : null;
|
|
465
|
+
assetEngineRef = null;
|
|
466
|
+
if (ownedAssetEngine) Promise.resolve(ownedAssetEngine).then((engine) => engine.destroy()).catch(() => {});
|
|
467
|
+
});
|
|
468
|
+
return {
|
|
469
|
+
libraries: () => {
|
|
470
|
+
const s = ctx.getState();
|
|
471
|
+
return s.libraryOrder.map((id) => s.libraries[id]).filter((l) => l != null);
|
|
472
|
+
},
|
|
473
|
+
library: (id) => ctx.getState().libraries[id] ?? null,
|
|
474
|
+
assets: (libraryId) => {
|
|
475
|
+
const s = ctx.getState();
|
|
476
|
+
if (libraryId) {
|
|
477
|
+
const library = s.libraries[libraryId];
|
|
478
|
+
return library ? library.assetIds.map((id) => s.assets[id]).filter((a) => a != null) : [];
|
|
479
|
+
}
|
|
480
|
+
return s.libraryOrder.flatMap((lid) => (s.libraries[lid]?.assetIds ?? []).map((id) => s.assets[id]).filter((a) => a != null));
|
|
481
|
+
},
|
|
482
|
+
asset: (id) => ctx.getState().assets[id] ?? null,
|
|
483
|
+
assetPreview: (id) => binaries.get(id)?.preview ?? null,
|
|
484
|
+
assetBytes: (id) => binaries.get(id)?.bytes ?? null,
|
|
485
|
+
libraryBytes: (id) => libraryBinaries.get(id) ?? null,
|
|
486
|
+
importLibraryPdf,
|
|
487
|
+
addAsset,
|
|
488
|
+
removeAsset,
|
|
489
|
+
removeLibrary,
|
|
490
|
+
armAsset,
|
|
491
|
+
disarm: (documentId) => ctx.forDocument(_embedpdf_plugin_annotation.AnnotationToken, documentId).disarmStamp()
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/reducer.ts
|
|
496
|
+
const initialStampState = () => ({
|
|
497
|
+
libraries: {},
|
|
498
|
+
libraryOrder: [],
|
|
499
|
+
assets: {}
|
|
500
|
+
});
|
|
501
|
+
/** Pure and serializable (kernel rule 1) — descriptors only; the bytes and
|
|
502
|
+
* previews live in the capability and never enter the store. */
|
|
503
|
+
const stampReducer = (state, action) => {
|
|
504
|
+
switch (action.type) {
|
|
505
|
+
case "LIBRARY_ADDED": return {
|
|
506
|
+
...state,
|
|
507
|
+
libraries: {
|
|
508
|
+
...state.libraries,
|
|
509
|
+
[action.library.id]: action.library
|
|
510
|
+
},
|
|
511
|
+
libraryOrder: [...state.libraryOrder, action.library.id]
|
|
512
|
+
};
|
|
513
|
+
case "LIBRARY_REMOVED": {
|
|
514
|
+
const library = state.libraries[action.libraryId];
|
|
515
|
+
if (!library) return state;
|
|
516
|
+
const libraries = { ...state.libraries };
|
|
517
|
+
delete libraries[action.libraryId];
|
|
518
|
+
const assets = { ...state.assets };
|
|
519
|
+
for (const id of library.assetIds) delete assets[id];
|
|
520
|
+
return {
|
|
521
|
+
...state,
|
|
522
|
+
libraries,
|
|
523
|
+
libraryOrder: state.libraryOrder.filter((id) => id !== action.libraryId),
|
|
524
|
+
assets
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
case "ASSET_ADDED": {
|
|
528
|
+
const library = state.libraries[action.asset.libraryId];
|
|
529
|
+
if (!library) return state;
|
|
530
|
+
return {
|
|
531
|
+
...state,
|
|
532
|
+
assets: {
|
|
533
|
+
...state.assets,
|
|
534
|
+
[action.asset.id]: action.asset
|
|
535
|
+
},
|
|
536
|
+
libraries: {
|
|
537
|
+
...state.libraries,
|
|
538
|
+
[library.id]: {
|
|
539
|
+
...library,
|
|
540
|
+
assetIds: [...library.assetIds, action.asset.id]
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
case "ASSET_REMOVED": {
|
|
546
|
+
const asset = state.assets[action.assetId];
|
|
547
|
+
if (!asset) return state;
|
|
548
|
+
const assets = { ...state.assets };
|
|
549
|
+
delete assets[action.assetId];
|
|
550
|
+
const library = state.libraries[asset.libraryId];
|
|
551
|
+
return {
|
|
552
|
+
...state,
|
|
553
|
+
assets,
|
|
554
|
+
libraries: library ? {
|
|
555
|
+
...state.libraries,
|
|
556
|
+
[library.id]: {
|
|
557
|
+
...library,
|
|
558
|
+
assetIds: library.assetIds.filter((id) => id !== action.assetId)
|
|
559
|
+
}
|
|
560
|
+
} : state.libraries
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
default: return state;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/types.ts
|
|
568
|
+
const StampToken = (0, _embedpdf_core.createCapabilityToken)("stamp");
|
|
569
|
+
//#endregion
|
|
570
|
+
//#region src/stamp.plugin.ts
|
|
571
|
+
/**
|
|
572
|
+
* The stamp plugin. WORKSPACE-scoped: libraries are shared across every open
|
|
573
|
+
* document; placement names its document explicitly (`armAsset(documentId, …)`)
|
|
574
|
+
* and delegates to that document's annotation plugin — no hard `requires`,
|
|
575
|
+
* because the annotation token is document-scoped and resolved lazily per
|
|
576
|
+
* placement (a viewer without the annotation plugin fails at the arm call,
|
|
577
|
+
* with the kernel's own missing-capability error).
|
|
578
|
+
*/
|
|
579
|
+
const stampPlugin = (config = {}) => (0, _embedpdf_core.definePlugin)({
|
|
580
|
+
id: "stamp",
|
|
581
|
+
token: StampToken,
|
|
582
|
+
scope: "workspace",
|
|
583
|
+
initialState: initialStampState,
|
|
584
|
+
reduce: stampReducer,
|
|
585
|
+
capability: (ctx) => createStampCapability(ctx, config)
|
|
586
|
+
});
|
|
587
|
+
//#endregion
|
|
588
|
+
exports.StampToken = StampToken;
|
|
589
|
+
exports.stampPlugin = stampPlugin;
|
|
590
|
+
|
|
591
|
+
//# sourceMappingURL=index.cjs.map
|