24eme/signaturepdf

Génération de PDF en full client side

ldubost opened this issue · 2 comments

Actuellement signaturepdf envoie les informations de signature au serveur et stocke le pdf sur le serveur pour ensuite génerer le PDF final avec du code serveur.

Dans le cadre d'une intégration de l'UI de signaturepdf dans CryptPad (une solution de drive chiffré de bout en bout permettant entre autre le stockage de PDF et le partage collaboratif), il est nécessaire de faire la génération de PDF client side.

L'objectif est d'ouvrir le PDF, d'y intégrer les signatures et annotations et de resauver le PDF

Ceci est réalisé avec pdf-lib.js. Le code est dans l'intégration CryptPad mais pourrait être reversé à signaturepdf:
https://github.com/xwiki-labs/cryptpad/tree/signpdf/www/sign

Le code est au début de prepareDoc ici:

https://github.com/xwiki-labs/cryptpad/blob/signpdf/www/sign/inner.js#L928
(La suite de la fonction concerne de la signature digitale qui est une autre modification).
Cela utilise pdf-lib pour insérer les élements (il est possible que tout les types d'insertions ne soit pas supportés actuellement)

   async function prepareDoc(pdfData, items) {
       var buffer = await pdfData.arrayBuffer();
       var pdfDoc = await pdflib.PDFDocument.load(buffer);
   var pageHeight = 1000;
       pdfDoc.setTitle(pdfTitle);
       items.forEach(async (item) => {
         var img = await pdfDoc.embedPng(item.data);
         var page = pdfDoc.getPages()[item.index]
         page.drawImage(img, { x: 0, y: 0, width: page.getWidth(), height: page.getHeight()})
         pageHeight = page.getHeight();
       });
       const modifiedPdfBytes = await pdfDoc.save({ useObjectStreams: false });

Il est à noter que comme cela fait le rendu des annotations avec l'API canvas, cela pose un problème de qualité des images insérées.. Ce problème a été résolu en faisant un resize du canvas uniquement pour la génération (afin de moins modifier le code):

// temporarey resize of canvas to improve rendering quality
resizeCanvas(4);
canvasEditions.forEach(function(canvasEdition, index) {
items.push({
data: canvasEdition.toDataURL(), index: index,
name: index + '.png', type: 'image/png'
});
})
// restore previous canvas size
resizeCanvas(0.25);
prepareDoc(pdfData, items).then(pdfBytes => {
var blob = new Blob([pdfBytes.buffer], { type: "application/pdf" })
blob.name = pdfTitle.replace(".pdf", "-signed.pdf");
APP.FM.handleFile(blob);
});