import PSPDFKitType, { AnnotationsUnion, SignatureFormField as SignatureFormFieldType } from "./index"; import { Instance, WidgetAnnotation, ToolbarItem } from "./index"; import { EnvelopeResponse, Envelope, User, Element, Document, IFunction } from "./interfaces"; declare const PSPDFKit: typeof PSPDFKitType const { List } = PSPDFKit.Immutable; const { Rect } = PSPDFKit.Geometry; const { SignatureFormField } = PSPDFKit.FormFields; const { DRAW, TYPE } = PSPDFKit.ElectronicSignatureCreationMode; const { DISABLED } = PSPDFKit.AutoSaveMode; export class App { public static Instance: Instance; public static currentDocument: Document; public static envelopeKey: string; public static UI: UI; public static Network: Network; public static Annotation: Annotation; // This function will be called in the ShowEnvelope.razor page // and will trigger loading of the Editor Interface public static async init(container: string, envelopeKey: string) { // Initialize classes console.debug("Initializing classes..") App.UI = new UI(); App.Network = new Network(); App.Annotation = new Annotation(); // Load the envelope from the database console.debug("Loading envelope from database..") const envelopeObject: EnvelopeResponse = await App.Network.getEnvelope(envelopeKey); console.debug(envelopeObject) App.envelopeKey = envelopeKey; App.currentDocument = envelopeObject.envelope.documents[0]; // Load the document from the filestore console.debug("Loading document from filestore") let arrayBuffer try { arrayBuffer = await App.Network.getDocument(envelopeKey, App.currentDocument.id); } catch (e) { console.error(e) } // Load PSPDFKit console.debug("Loading PSPDFKit..") App.Instance = await App.UI.loadPSPDFKit(arrayBuffer, container) App.UI.configurePSPDFKit(this.Instance, App.handleClick) // Load annotations into PSPDFKit console.debug("Loading annotations..") const annotations = App.Annotation.createAnnotations(App.currentDocument) const createdAnnotations = await App.Instance.create(annotations) } public static async handleClick(eventType: string) { switch (eventType) { case "RESET": await App.handleReset(null) break; case "FINISH": await App.handleFinish(null) break; } } public static async handleFinish(event: any) { await App.Instance.save(); // Export annotation data and save to database const json = await App.Instance.exportInstantJSON() console.log(json); console.log(JSON.stringify(json)); const result = await App.Network.postEnvelope(App.envelopeKey, App.currentDocument.id, JSON.stringify(json)) // Flatten the annotations and save the document to disk /* const buffer = await App.Instance.exportPDF({ flatten: true }); const result = await App.Network.postDocument(App.envelopeKey, App.currentDocument.id, buffer); console.log(result) */ } public static async handleReset(event: any) { if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?")) { const result = App.Annotation.deleteAnnotations(App.Instance) } } private static async downloadDocument() { const buffer = await App.Instance.exportPDF({ flatten: true }); const supportsDownloadAttribute = HTMLAnchorElement.prototype.hasOwnProperty("download"); const blob = new Blob([buffer], { type: "application/pdf" }); if (!supportsDownloadAttribute) { const reader = new FileReader(); reader.onloadend = function () { const dataUrl = reader.result; downloadPdf(dataUrl); }; reader.readAsDataURL(blob); } else { const objectUrl = window.URL.createObjectURL(blob); downloadPdf(objectUrl); window.URL.revokeObjectURL(objectUrl); } function downloadPdf(blob) { const a = document.createElement("a"); a.href = blob; a.style.display = "none"; a.download = "download.pdf"; a.setAttribute("download", "download.pdf"); document.body.appendChild(a); a.click(); document.body.removeChild(a); } } } class Annotation { public createAnnotations(document: Document): AnnotationsUnion[] { const annotations: any[] = []; document.elements.forEach((element: Element) => { console.log("Creating annotation for element", element.id) const [annotation, formField] = this.createAnnotationFromElement(element) annotations.push(annotation); annotations.push(formField); }) return annotations; } public async deleteAnnotations(instance: Instance): Promise { let pageAnnotations = ( await Promise.all(Array.from({ length: instance.totalPageCount }).map((_, pageIndex) => instance.getAnnotations(pageIndex) )) ).flatMap((annotations) => annotations.reduce((acc, annotation) => acc.concat(annotation), []) ).filter((annotation) => !!annotation.isSignature); //deleting all Annotations return await instance.delete(pageAnnotations); } private createAnnotationFromElement(element: Element): [annotation: WidgetAnnotation, formField: SignatureFormFieldType] { const id = PSPDFKit.generateInstantId() const width = this.inchToPoint(element.width) const height = this.inchToPoint(element.height) const top = this.inchToPoint(element.top) - (height / 2) const left = this.inchToPoint(element.left) - (width / 2) const page = element.page - 1 const annotation: WidgetAnnotation = this.createSignatureAnnotation(id, width, height, top, left, page) console.log(annotation) const formField = new SignatureFormField({ name: id, annotationIds: List([annotation.id]) }) console.log(formField) return [annotation, formField] } private createSignatureAnnotation(id: string, width: number, height: number, top: number, left: number, pageIndex: number): WidgetAnnotation { const annotation = new PSPDFKit.Annotations.WidgetAnnotation({ id: id, pageIndex: pageIndex, formFieldName: id, boundingBox: new Rect({ width, height, top, left }) }) return annotation } private inchToPoint(inch: number): number { return inch * 72; } } class Network { public getEnvelope(envelopeKey: string): Promise { return fetch(`/api/envelope/${envelopeKey}`, { credentials: "include" }) .then(res => res.json()); } public getDocument(envelopeKey: string, documentId: number): Promise { return fetch(`/api/document/${envelopeKey}?index=${documentId}`, { credentials: "include" }) .then(res => res.arrayBuffer()); } public postDocument(envelopeKey: string, documentId: number, buffer: ArrayBuffer): Promise { return fetch(`/api/document/${envelopeKey}/${documentId}`, { credentials: "include", method: "POST", body: buffer }) .then(res => res.json()); } public postEnvelope(envelopeKey: string, documentId: number, jsonString: string): Promise { const options: RequestInit = { credentials: "include", method: "POST", body: jsonString } return fetch(`/api/envelope/${envelopeKey}?index=${documentId}`, options) .then(res => { console.log(res) res.json() }); } } class UI { public allowedToolbarItems: string[] = [ "sidebar-thumbnails", "sidebar-document-ouline", "sidebar-bookmarks", "pager", "pan", "zoom-out", "zoom-in", "zoom-mode", "spacer", "search" ] // Load the PSPDFKit UI by setting a target element as the container to render in // and a arraybuffer which represents the document that should be displayed. public loadPSPDFKit(arrayBuffer: ArrayBuffer, container: string): Promise { return PSPDFKit.load({ container: container, document: arrayBuffer, autoSaveMode: DISABLED, annotationPresets: this.getPresets(), electronicSignatures: { creationModes: [DRAW, TYPE] }, isEditableAnnotation: function (annotation: WidgetAnnotation) { // Check if the annotation is a signature // This will allow new signatures, but not allow edits. return !annotation.isSignature; } }) } public configurePSPDFKit(instance: Instance, handler: any) { instance.addEventListener("annotations.load", (loadedAnnotations) => { console.log("annotations loaded", loadedAnnotations.toJS()); }) instance.addEventListener("annotations.change", () => { console.log("annotations changed") }) instance.addEventListener("annotations.create", async (createdAnnotations) => { console.log("annotations created"); }) const toolbarItems = this.getToolbarItems(instance, handler) instance.setToolbarItems(toolbarItems) console.debug("PSPDFKit configured!"); } public getToolbarItems(instance: Instance, handler: any): ToolbarItem[] { const customItems = this.getCustomItems(handler) const defaultItems: Array = this.getDefaultItems(instance.toolbarItems) return defaultItems.concat(customItems) } private getCustomItems = function (callback: any) { const customItems: ToolbarItem[] = [ { type: "custom", id: "button-reset", title: "Zurücksetzen", onPress() { callback("RESET") }, icon: ` ` }, { type: "custom", id: "button-finish", title: "Abschließen", onPress() { callback("FINISH") }, icon: ` ` } ] return customItems } private getDefaultItems(items: ToolbarItem[]): ToolbarItem[] { return items.filter((item) => this.allowedToolbarItems.includes(item.type)) } private getPresets() { const annotationPresets = PSPDFKit.defaultAnnotationPresets; annotationPresets.ink = { lineWidth: 10 }; annotationPresets.widget = { readOnly: true } return annotationPresets; } }