24eme / signaturepdf

Free web software for signing PDFs and also organize pages, edit medata and compress pdf
https://pdf.24eme.fr
GNU Affero General Public License v3.0
369 stars 51 forks source link

Génération de PDF en full client side #45

Open ldubost opened 1 year ago

ldubost commented 1 year ago

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

ldubost commented 1 year ago

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 });
ldubost commented 1 year ago

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); });