Compare commits

...

2 Commits

Author SHA1 Message Date
Jonathan Jenne
dfadcff710 save annotation data as json and sent to server 2023-10-09 13:05:31 +02:00
Jonathan Jenne
8c629691d9 loading works again 2023-10-09 11:22:28 +02:00
6 changed files with 230 additions and 145 deletions

View File

@@ -5,12 +5,50 @@ using EnvelopeGenerator.Web.Services;
using Microsoft.Extensions.Primitives;
using System.IO.Pipelines;
using System.Reflection.Metadata.Ecma335;
using System.Text;
namespace EnvelopeGenerator.Web.Handler
{
public class FileHandler
{
public async static Task<IResult> HandleFileDownload(HttpContext ctx, DatabaseService database, LoggingService logging)
/// <summary>
/// URL: GET /api/envelope/{envelopeKey}
///
/// Returns a
/// </summary>
/// <returns></returns>
public static IResult HandleGetEnvelope(HttpContext ctx, DatabaseService database, LoggingService logging)
{
var logger = logging.LogConfig.GetLogger("FileHandler");
try
{
logger.Info("Handling envelope loading.");
// Load Envelope from EnvelopeKey
string envelopeKey = EnsureValidEnvelopeKey(logger, ctx.Request);
EnvelopeResponse r = database.LoadEnvelope(envelopeKey);
// Return the envelope and additional data as json
return Results.Json(r);
}
catch (Exception e)
{
// Better error handling & reporting
logger.Error(e);
return Results.Problem(e.Message);
}
}
/// <summary>
/// URL: GET /api/document/{envelopeKey}?index={documentIndex}
///
/// Returns a document for the supplied EnvelopeKey and Document Id / Index
/// </summary>
/// <returns>file buffer of the requested document</returns>
public async static Task<IResult> HandleGetDocument(HttpContext ctx, DatabaseService database, LoggingService logging)
{
var logger = logging.LogConfig.GetLogger("FileHandler");
@@ -42,7 +80,7 @@ namespace EnvelopeGenerator.Web.Handler
}
}
public async static Task<IResult> HandleFileUpload(HttpContext ctx, DatabaseService database, LoggingService logging)
public async static Task<IResult> HandlePostDocument(HttpContext ctx, DatabaseService database, LoggingService logging)
{
var logger = logging.LogConfig.GetLogger("FileHandler");
@@ -72,13 +110,16 @@ namespace EnvelopeGenerator.Web.Handler
}
}
public async static Task<IResult> HandleGetData(HttpContext ctx, DatabaseService database, LoggingService logging)
public async static Task<IResult> HandlePostEnvelope(HttpContext ctx, DatabaseService database, LoggingService logging)
{
var logger = logging.LogConfig.GetLogger("FileHandler");
try
{
logger.Info("Handling file download.");
logger.Info("Handling envelope saving.");
// Load Envelope from EnvelopeKey
string envelopeKey = EnsureValidEnvelopeKey(logger, ctx.Request);
@@ -86,59 +127,27 @@ namespace EnvelopeGenerator.Web.Handler
// Get the document Index
int documentId = EnsureValidDocumentIndex(logger, ctx.Request);
var document = GetDocument(r.Envelope, documentId);
var document = GetDocument(r.Envelope, documentId);
// Load the document from disk
var bytes = await File.ReadAllBytesAsync(document.Filepath);
logger.Info("Serving file, size: [{0}]", bytes.Length);
var annotationData = EnsureValidAnnotationData(logger, ctx.Request);
// Return the envelope and additional data as json
return Results.Json(r);
if (annotationData == null)
{
throw new ArgumentNullException("AnnotationData");
}
// TODO: Save annotations to database
return Results.Ok();
}
catch (Exception e)
{
// Better error handling & reporting
logger.Error(e);
return Results.Problem();
}
}
}
public async static Task<IResult> HandlePostData(HttpContext ctx, DatabaseService database, LoggingService logging)
{
var logger = logging.LogConfig.GetLogger("FileHandler");
try
{
logger.Info("Handling file download.");
// Load Envelope from EnvelopeKey
string envelopeKey = EnsureValidEnvelopeKey(logger, ctx.Request);
EnvelopeResponse r = database.LoadEnvelope(envelopeKey);
// Get the document Index
int documentId = EnsureValidDocumentIndex(logger, ctx.Request);
var document = GetDocument(r.Envelope, documentId);
}
catch (Exception e)
{
// Better error handling & reporting
logger.Error(e);
return Results.Problem();
}
var envelopeKey = ctx.Request.RouteValues["envelopeKey"] as string;
var documentIdString = ctx.Request.RouteValues["documentId"] as string;
if (int.TryParse(documentIdString, out int documentId) == false)
{
return Results.Problem();
}
database.LoadDocument(documentId);
return Results.Ok();
}
private static int EnsureValidDocumentIndex(Logger logger, HttpRequest request)
@@ -181,6 +190,26 @@ namespace EnvelopeGenerator.Web.Handler
return envelopeKey;
}
private static async Task<string> EnsureValidAnnotationData(Logger logger, HttpRequest request)
{
logger.Debug("Parsing AnnotationData..");
try
{
using MemoryStream ms = new();
await request.BodyReader.CopyToAsync(ms);
var bytes = ms.ToArray();
return Encoding.UTF8.GetString(bytes);
}
catch (Exception e)
{
logger.Error(e);
return null;
}
}
private static EnvelopeDocument GetDocument(Common.Envelope envelope, int documentId)
{
var document = envelope.Documents.
@@ -192,8 +221,5 @@ namespace EnvelopeGenerator.Web.Handler
return document;
}
}
}

View File

@@ -15,7 +15,7 @@
if (firstRender)
{
var module = await JS.InvokeAsync<IJSObjectReference>("import", "./js/app.js");
await module.InvokeVoidAsync("App.loadPDFFromUrl", "#container", EnvelopeReceiverId);
await module.InvokeVoidAsync("App.init", "#container", EnvelopeReceiverId);
}
}
}

View File

@@ -35,10 +35,10 @@ app.UseStaticFiles();
app.UseRouting();
// Add file download endpoint
app.MapGet("/api/document/{envelopeKey}", FileHandler.HandleFileDownload);
app.MapPost("/api/document/{envelopeKey}/{documentId}", FileHandler.HandleFileUpload);
app.MapGet("/api/envelope/{envelopeKey}", FileHandler.HandleGetData);
app.MapPost("/api/envelope/{envelopeKey}/{documentId}", FileHandler.HandlePostData);
app.MapGet("/api/document/{envelopeKey}", FileHandler.HandleGetDocument);
app.MapPost("/api/document/{envelopeKey}", FileHandler.HandlePostDocument);
app.MapGet("/api/envelope/{envelopeKey}", FileHandler.HandleGetEnvelope);
app.MapPost("/api/envelope/{envelopeKey}", FileHandler.HandlePostEnvelope);
// Blazor plumbing
app.MapBlazorHub();

View File

@@ -21,43 +21,41 @@ export class App {
// This function will be called in the ShowEnvelope.razor page
// and will trigger loading of the Editor Interface
public static async loadPDFFromUrl(container: string, envelopeKey: string) {
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();
console.debug("Loading PSPDFKit..");
// Load the envelope from the database
console.debug("Loading envelope from database..")
const envelopeObject: EnvelopeResponse = await App.Network.getEnvelope(envelopeKey);
console.debug(envelopeObject)
const envelopeObject: EnvelopeResponse = await App.Network.loadData(envelopeKey);
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.loadDocument(envelopeKey, App.currentDocument.id);
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)
console.debug(envelopeObject.envelope);
console.debug("PSPDFKit configured!");
const annotations: any[] = [];
App.currentDocument.elements.forEach(function (element: Element) {
console.log("Creating annotation for element", element.id)
const [annotation, formField] = App.Annotation.createAnnotationFromElement(element)
annotations.push(annotation);
annotations.push(formField);
})
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)
console.debug(createdAnnotations)
}
public static async handleClick(eventType: string) {
@@ -73,15 +71,21 @@ export class App {
}
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) {
@@ -121,7 +125,33 @@ export class App {
}
class Annotation {
public createAnnotationFromElement(element: Element): [annotation: WidgetAnnotation, formField: SignatureFormFieldType] {
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<any> {
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)
@@ -140,18 +170,6 @@ class Annotation {
return [annotation, formField]
}
public async deleteAnnotations(instance: Instance): Promise<any> {
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 createSignatureAnnotation(id: string, width: number, height: number, top: number, left: number, pageIndex: number): WidgetAnnotation {
const annotation = new PSPDFKit.Annotations.WidgetAnnotation({
id: id,
@@ -169,22 +187,34 @@ class Annotation {
}
class Network {
// Makes a call to the supplied url and fetches the binary response as an array buffer
public loadDocument(envelopeKey: string, documentId: number): Promise<ArrayBuffer> {
return fetch(`/api/file/${envelopeKey}?id=${documentId}`, { credentials: "include" })
.then(res => res.arrayBuffer());
}
// Makes a call to the supplied url and fetches the json response as an object
public loadData(envelopeKey): Promise<any> {
public getEnvelope(envelopeKey: string): Promise<any> {
return fetch(`/api/envelope/${envelopeKey}`, { credentials: "include" })
.then(res => res.json());
}
public getDocument(envelopeKey: string, documentId: number): Promise<ArrayBuffer> {
return fetch(`/api/document/${envelopeKey}?index=${documentId}`, { credentials: "include" })
.then(res => res.arrayBuffer());
}
public postDocument(envelopeKey: string, documentId: number, buffer: ArrayBuffer): Promise<any> {
return fetch(`/api/upload/${envelopeKey}/${documentId}`, { credentials: "include", method: "POST", body: buffer })
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<any> {
const options: RequestInit = {
credentials: "include",
method: "POST",
body: jsonString
}
return fetch(`/api/envelope/${envelopeKey}?index=${documentId}`, options)
.then(res => {
console.log(res)
res.json()
});
}
}
@@ -236,6 +266,8 @@ class UI {
const toolbarItems = this.getToolbarItems(instance, handler)
instance.setToolbarItems(toolbarItems)
console.debug("PSPDFKit configured!");
}
public getToolbarItems(instance: Instance, handler: any): ToolbarItem[] {

View File

@@ -44,25 +44,31 @@ var App = /** @class */ (function () {
}
// This function will be called in the ShowEnvelope.razor page
// and will trigger loading of the Editor Interface
App.loadPDFFromUrl = function (container, envelopeKey) {
App.init = function (container, envelopeKey) {
return __awaiter(this, void 0, void 0, function () {
var envelopeObject, arrayBuffer, e_1, _a, annotations, createdAnnotations;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
// Initialize classes
console.debug("Initializing classes..");
App.UI = new UI();
App.Network = new Network();
App.Annotation = new Annotation();
console.debug("Loading PSPDFKit..");
return [4 /*yield*/, App.Network.loadData(envelopeKey)];
// Load the envelope from the database
console.debug("Loading envelope from database..");
return [4 /*yield*/, App.Network.getEnvelope(envelopeKey)];
case 1:
envelopeObject = _b.sent();
console.debug(envelopeObject);
App.envelopeKey = envelopeKey;
App.currentDocument = envelopeObject.envelope.documents[0];
// Load the document from the filestore
console.debug("Loading document from filestore");
_b.label = 2;
case 2:
_b.trys.push([2, 4, , 5]);
return [4 /*yield*/, App.Network.loadDocument(envelopeKey, App.currentDocument.id)];
return [4 /*yield*/, App.Network.getDocument(envelopeKey, App.currentDocument.id)];
case 3:
arrayBuffer = _b.sent();
return [3 /*break*/, 5];
@@ -71,24 +77,19 @@ var App = /** @class */ (function () {
console.error(e_1);
return [3 /*break*/, 5];
case 5:
// Load PSPDFKit
console.debug("Loading PSPDFKit..");
_a = App;
return [4 /*yield*/, App.UI.loadPSPDFKit(arrayBuffer, container)];
case 6:
_a.Instance = _b.sent();
App.UI.configurePSPDFKit(this.Instance, App.handleClick);
console.debug(envelopeObject.envelope);
console.debug("PSPDFKit configured!");
annotations = [];
App.currentDocument.elements.forEach(function (element) {
console.log("Creating annotation for element", element.id);
var _a = App.Annotation.createAnnotationFromElement(element), annotation = _a[0], formField = _a[1];
annotations.push(annotation);
annotations.push(formField);
});
// Load annotations into PSPDFKit
console.debug("Loading annotations..");
annotations = App.Annotation.createAnnotations(App.currentDocument);
return [4 /*yield*/, App.Instance.create(annotations)];
case 7:
createdAnnotations = _b.sent();
console.debug(createdAnnotations);
return [2 /*return*/];
}
});
@@ -121,7 +122,7 @@ var App = /** @class */ (function () {
};
App.handleFinish = function (event) {
return __awaiter(this, void 0, void 0, function () {
var json, buffer, result;
var json, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, App.Instance.save()];
@@ -131,13 +132,17 @@ var App = /** @class */ (function () {
case 2:
json = _a.sent();
console.log(json);
return [4 /*yield*/, App.Instance.exportPDF({ flatten: true })];
console.log(JSON.stringify(json));
return [4 /*yield*/, 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)
*/
];
case 3:
buffer = _a.sent();
return [4 /*yield*/, App.Network.postDocument(App.envelopeKey, App.currentDocument.id, buffer)];
case 4:
result = _a.sent();
console.log(result);
return [2 /*return*/];
}
});
@@ -147,7 +152,7 @@ var App = /** @class */ (function () {
return __awaiter(this, void 0, void 0, function () {
var result;
return __generator(this, function (_a) {
if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zurücksetzen?")) {
if (confirm("Wollen Sie das Dokument und alle erstellten Signaturen zur<EFBFBD>cksetzen?")) {
result = App.Annotation.deleteAnnotations(App.Instance);
}
return [2 /*return*/];
@@ -198,21 +203,16 @@ export { App };
var Annotation = /** @class */ (function () {
function Annotation() {
}
Annotation.prototype.createAnnotationFromElement = function (element) {
var id = PSPDFKit.generateInstantId();
var width = this.inchToPoint(element.width);
var height = this.inchToPoint(element.height);
var top = this.inchToPoint(element.top) - (height / 2);
var left = this.inchToPoint(element.left) - (width / 2);
var page = element.page - 1;
var annotation = this.createSignatureAnnotation(id, width, height, top, left, page);
console.log(annotation);
var formField = new SignatureFormField({
name: id,
annotationIds: List([annotation.id])
Annotation.prototype.createAnnotations = function (document) {
var _this = this;
var annotations = [];
document.elements.forEach(function (element) {
console.log("Creating annotation for element", element.id);
var _a = _this.createAnnotationFromElement(element), annotation = _a[0], formField = _a[1];
annotations.push(annotation);
annotations.push(formField);
});
console.log(formField);
return [annotation, formField];
return annotations;
};
Annotation.prototype.deleteAnnotations = function (instance) {
return __awaiter(this, void 0, void 0, function () {
@@ -234,6 +234,22 @@ var Annotation = /** @class */ (function () {
});
});
};
Annotation.prototype.createAnnotationFromElement = function (element) {
var id = PSPDFKit.generateInstantId();
var width = this.inchToPoint(element.width);
var height = this.inchToPoint(element.height);
var top = this.inchToPoint(element.top) - (height / 2);
var left = this.inchToPoint(element.left) - (width / 2);
var page = element.page - 1;
var annotation = this.createSignatureAnnotation(id, width, height, top, left, page);
console.log(annotation);
var formField = new SignatureFormField({
name: id,
annotationIds: List([annotation.id])
});
console.log(formField);
return [annotation, formField];
};
Annotation.prototype.createSignatureAnnotation = function (id, width, height, top, left, pageIndex) {
var annotation = new PSPDFKit.Annotations.WidgetAnnotation({
id: id,
@@ -251,20 +267,30 @@ var Annotation = /** @class */ (function () {
var Network = /** @class */ (function () {
function Network() {
}
// Makes a call to the supplied url and fetches the binary response as an array buffer
Network.prototype.loadDocument = function (envelopeKey, documentId) {
return fetch("/api/file/".concat(envelopeKey, "?id=").concat(documentId), { credentials: "include" })
.then(function (res) { return res.arrayBuffer(); });
};
// Makes a call to the supplied url and fetches the json response as an object
Network.prototype.loadData = function (envelopeKey) {
Network.prototype.getEnvelope = function (envelopeKey) {
return fetch("/api/envelope/".concat(envelopeKey), { credentials: "include" })
.then(function (res) { return res.json(); });
};
Network.prototype.getDocument = function (envelopeKey, documentId) {
return fetch("/api/document/".concat(envelopeKey, "?index=").concat(documentId), { credentials: "include" })
.then(function (res) { return res.arrayBuffer(); });
};
Network.prototype.postDocument = function (envelopeKey, documentId, buffer) {
return fetch("/api/upload/".concat(envelopeKey, "/").concat(documentId), { credentials: "include", method: "POST", body: buffer })
return fetch("/api/document/".concat(envelopeKey, "/").concat(documentId), { credentials: "include", method: "POST", body: buffer })
.then(function (res) { return res.json(); });
};
Network.prototype.postEnvelope = function (envelopeKey, documentId, jsonString) {
var options = {
credentials: "include",
method: "POST",
body: jsonString
};
return fetch("/api/envelope/".concat(envelopeKey, "?index=").concat(documentId), options)
.then(function (res) {
console.log(res);
res.json();
});
};
return Network;
}());
var UI = /** @class */ (function () {
@@ -286,7 +312,7 @@ var UI = /** @class */ (function () {
{
type: "custom",
id: "button-reset",
title: "Zurücksetzen",
title: "Zur<EFBFBD>cksetzen",
onPress: function () {
callback("RESET");
},
@@ -295,7 +321,7 @@ var UI = /** @class */ (function () {
{
type: "custom",
id: "button-finish",
title: "Abschließen",
title: "Abschlie<EFBFBD>en",
onPress: function () {
callback("FINISH");
},
@@ -339,6 +365,7 @@ var UI = /** @class */ (function () {
}); });
var toolbarItems = this.getToolbarItems(instance, handler);
instance.setToolbarItems(toolbarItems);
console.debug("PSPDFKit configured!");
};
UI.prototype.getToolbarItems = function (instance, handler) {
var customItems = this.getCustomItems(handler);

File diff suppressed because one or more lines are too long