Extraxt data on PDF and save it in a collection

Question:
How to extract data and save it?

Product:
Wix Editor

What are you trying to achieve:
Hello everyone

Thanks to hp1’s topic (Generate and download a custom PDF with pdf-lib), I managed to create PDFs from one of my pages (Customer Invoice)with pdf-lib. It works perfectly. I can extract my customer data (first and last name, invoice amount, tax, adress, etc.) and create my PDF. Thank you again.

However, I’m facing some difficulties:

1/ How can I export my dataset data to this PDF? (Multiple data): My client has several services : how to export them? (A table?)
2/ How can I embed an image? (.src?) (take look at > //LOGO section : i would like to insert the source of my logo: actually i use a model of pdf-lib
3/ I created a collection called “Mes Factures” with a text field “ResaNum” and a document field “Invoice Name”. My PDF is currently downloaded and saved on my computer and in WIX’s “Media Collection.”
How do I integrate my PDF document generated and saved from “Facture client” page into my “Mes Factures” collection? (Actyually i import it manually : but i would like to do it with a code automatically)

Thank you all for any help you can provide.

Here the code i use to generate and save my pdf :

$w(‘#GenererFacture’).onClick(async(event) => {

> // Nom FICHIER

const fileName =$w('#text235').text+" "+$w('#text4').text;

*> // Nouveau PDF

const pdfDoc = await PDFDocument.create();

> // POLICE

const timesRomanFont = await pdfDoc.embedFont(StandardFonts.TimesRoman);

// Add a blank page to the document
const page = pdfDoc.addPage();

// Get the width and height of the page
const { width, height } = page.getSize();

> //LOGO

const jpgUrl = ‘https://pdf-lib.js.org/assets/cat_riding_unicorn.jpg
//const jpgUrl = ‘file:///Users/user1/Desktop/Logo.jpg’
//const jpgUrl=$w(‘#logo’).src
const jpgImageBytes = await fetch(jpgUrl).then((res) => res.arrayBuffer())

const jpgImage = await pdfDoc.embedJpg(jpgImageBytes)
const jpgDims = jpgImage.scale(0.5)

page.drawImage(jpgImage, {
x: 10,
y: 720,
width: 90,
height: 90,
//rotate: degrees(30),
opacity: 0.75,
})

> //ENTETE GAUCHE

page.drawText(“CAMPING LE PRE DU ROI”, { x: 10, y: 720, size: 12, font: timesRomanFont })
page.drawText(“Route des Bouchets”, { x: 10, y: 700, size: 12, font: timesRomanFont })
page.drawText(“89 660 MAILLY LE CHATEAU”, { x: 10, y: 680, size: 12, font: timesRomanFont })
page.drawText(“03.86.81.44.37”, { x: 10, y: 660, size: 12, font: timesRomanFont })
page.drawText(“camping-maillylechateau@orange.fr”, { x: 10, y: 650, size: 12, font: timesRomanFont})

> //ENTETE DROIT

page.drawText($w(‘#text235’).text, { x: 420, y: 800, size: 12, font: timesRomanFont })
page.drawText($w(‘#text4’).text, { x: 480, y: 800, size: 12, font: timesRomanFont })
page.drawText($w(‘#text252’).text, { x: 420, y: 780, size: 12, font: timesRomanFont })
page.drawText($w(‘#text241’).text, { x: 420, y: 760, size: 12, font: timesRomanFont })
page.drawText($w(‘#text242’).text, { x: 480, y: 760, size: 12, font: timesRomanFont})

> // TRAIT DE SEPARATION HAUT (NUM FACTURE)

page.drawLine({
start:{ x: 10, y: 630, },
end:{ x: 550, y: 630, },
thickness: 2,
//color: rgb(0.75, 0.2, 0.2),
opacity: 0.75,
})

> //NUM FACTURE

page.drawText(“VOTRE FACTURE N°…”+ $w(‘#text240’).text, { x: 45, y: 605, size: 16, font: timesRomanFont})

> // TRAIT DE SEPARATION BAS (NUM FACTURE)

page.drawLine({
start:{ x: 10, y: 580, },
end:{ x: 550, y: 580, },
thickness: 2,
//color: rgb(0.75, 0.2, 0.2),
opacity: 0.75,
})

> //DETAILS FACTURE

page.drawText(“PRESTATIONS FACTUREES”, { x: 10, y: 500, size: 15, font: timesRomanFont})

> // TRAIT DE SEPARATION BAS DE PAGE

page.drawLine({
start:{ x: 10, y: 205, },
end:{ x: 550, y: 205, },
thickness: 1,
//color: rgb(0.75, 0.2, 0.2),
opacity: 0.75,
})

> // TOTAL FACTURE A REGLER BAS DE PAGE

page.drawText("TOTAL HT ", { x: 10, y: 180, size: 12, font: timesRomanFont })
page.drawText($w(‘#input12’).value+“€”, { x: 480, y: 180, size: 12, font: timesRomanFont })
page.drawText(“T.V.A”, { x: 10, y: 160, size: 12, font: timesRomanFont })
page.drawText($w(‘#input13’).value+“€”, { x: 480, y: 160, size: 12, font: timesRomanFont })
page.drawText(“Sous-Total TTC”, { x: 10, y: 140, size: 12, font: timesRomanFont })
page.drawText($w(‘#input15’).value+“€”, { x: 480, y: 140, size: 15, font: timesRomanFont,color:rgb(1,0,0) })

> // TRAIT DE SEPARATION BAS DE PAGE

page.drawLine({
start:{ x: 475, y: 125, },
end:{ x:550, y: 125, },
thickness: 1,
//color: rgb(0.75, 0.2, 0.2),
opacity: 0.75,
})

page.drawText("Réductions (Séjour long & Groupe) ", { x: 10, y: 100, size: 12, font: timesRomanFont })
page.drawText($w(‘#input20’).value+“€”, { x: 480, y: 100, size: 12, font: timesRomanFont })
page.drawText("Taxes de séjour ", { x: 10, y: 80, size: 12, font: timesRomanFont })
page.drawText($w(‘#input17’).value+“€”, { x: 480, y: 80, size: 12, font: timesRomanFont })

> // TRAIT DE SEPARATION BAS DE PAGE

page.drawLine({
start:{ x: 475, y: 60, },
end:{ x:550, y: 60, },
thickness: 1,
//color: rgb(0.75, 0.2, 0.2),
opacity: 0.75,
})

page.drawText("TOTAL A REGLER: ", { x: 10, y: 40, size: 12, font: timesRomanFont })
page.drawText($w(‘#input18’).value+“€”, { x: 480, y: 40, size: 15, font: timesRomanFont,color:rgb(1,0,0) })

// Serialize the PDFDocument to Base64
const pdfDataUri = await pdfDoc.saveAsBase64({ dataUri: true });
const dataUriPrefix = 'data:application/pdf;base64,';
const base64 = pdfDataUri.slice(dataUriPrefix.length);

> // SAUVEGARDE ET DL DU FICHIER

savePDF(base64, fileName)
    .then(downloadUrl => {
        console.log(downloadUrl);
        wixLocation.to(downloadUrl); 
    })
    .catch((error) => {
        console.error("Erreur pendant l'enregistrement du PDF: ", ERREUR);
    });

})

To answer your queries one by one:

Here’s what the author of pdf-lib.js had to say about generating tables:

Since there is no direct way to ‘generate’ a table dynamically, you will have to get creative and use a lot of math and calculations to draw an actual, dynamic table table layout. Or you can use a helper library such as pdfkit-table which I haven’t used personally so I’m not sure if that’ll work exactly as expected. But you can test it out if you want to, and AI can help you with the coding bit.


Very simple. Upload your logo to your site’s Media Manager (if you haven’t already) and fetch the URL of the image like so:

And then simply replace the default URL in the LOGO section of your code to the one you had copied earlier - which is your logo’s URL, and that’s it.


Which backend API are you using to save your PDF in the media manager? You haven’t shown that in your code. Also what is the Field Type of the column in your CMS where you want to save this PDF?

The way you have divided your code into sections and copy-pasted each section here might seem that it would be easy for us to debug, but it’s not the right way to do so. Instead paste your entire code inside the code embed </> button and divide your frontend and backend code files separately. If you want to break lengthy code into sections, use comments to denote that. Don’t break it into individual sinppets.

HI Pratham,

Thanks a lot for your answer.

For first question : i’ll try it…
For the image, effectively so simple – sorry. :stuck_out_tongue: : it’s now OK. Thanks
For the last question : 2 fields (See below)

  • Ref Resa : text
  • Facture : Document

In my first message : it was frontend.

Here my backend code :

import { mediaManager } from 'wix-media-backend';

export function savePDF(base64, fileName) {
    return new Promise((resolve, reject) => {
        
        uploadPDF(base64, fileName) 
        .then((file) => { 

            getDownloadUrl(file.fileUrl) 
                .then((downloadUrl) => { 
                    resolve(downloadUrl); 
                    const fileArray = [file.fileUrl]; 
                    setTimeout(() => { 
                          trashFiles(fileArray); 
                    }, 20000); 
                }) 
                .catch((error) => { 
                    reject(error); 
                }); 
        }) 
        .catch((error) => { 
            reject(error); 
        }); 

    });
}

export function trashFiles(fileUrls) {
return mediaManager.moveFilesToTrash(fileUrls)
.then(() => {
console.log('Success! Le fichier temporaire a bien été supprimé..');
})
.catch((error) => {
console.error(error);
})
}

export function uploadPDF(base64, fileName) {
    // create Buffer from Base64
    const buffer = Buffer.from(base64, 'base64');

    return mediaManager.upload(
        "/MesFactures",
        buffer,
        fileName + ".pdf", {
            "mediaOptions": {
                "mimeType": "application/pdf",
                "mediaType": "document"
            },
            "metadataOptions": {
                "isPrivate": false,
                "isVisitorUpload": false,
                "context": {
                    "someKey1": "someValue1",
                    "someKey2": "someValue2"
                }
            }
        }
    );
}

export async function getDownloadUrl(fileUrl) {
    const myFileDownloadUrl = await mediaManager.getDownloadUrl(fileUrl, 20);
    return myFileDownloadUrl;
}


So you’re uploading the PDF to the Media Manager, generating a temporary download URL, and then deleting the PDF after 20 seconds? (I’m not even sure how the function would continue to execute without timing out with such a delay in place… Definitely not the best practice)

If you want the file to be accessible from the CMS, you will probably NOT want to delete it from the Media Manager, because even though it is linked through the CMS, the actual source of that file is still your site’s Media Manager and deleting it from the location it is stored in is going to make the file inaccessible from then CMS too.

And how are you generating the Ref Resa field? If you want the PDF to get stored in the collection automatically then you’ll also need to generate some value for the text field too, else that will remain blank.

Effectively, i deleted the temporary file after 20 sec … but that was before.
Now i don’t do it anymore and keep it in the media manager to take it back if i need it.
Yes i’ insert a ref resa in relation with my invoice and the document to be attached (taken from another collection)
For my first problem, it’s ok, i bypassed it by query and put it in the pdf if results>0 : it’s ok
i had to make line by line but it’s ok
I juste have now to find a solution to put the generated document in my collection “Mesfactures”

I haven’t edited your entire backend code, just updated the savePDF function to demonstrate how to store the PDF document in your collection.

I am assuming your Collection ID is Mesfactures
If its not that, then change the ID and try it out.

import { mediaManager } from 'wix-media-backend';
import wixData from 'wix-data';

export function savePDF(base64, fileName) {
    return new Promise((resolve, reject) => {

        uploadPDF(base64, fileName)
            .then((file) => {

                let toInsert = {
                    facture: file.fileUrl,
                };

                wixData
                    .insert("Mesfactures", toInsert, { suppressAuth: true })
                    .then((item) => {

                        getDownloadUrl(file.fileUrl)
                            .then((downloadUrl) => {
                                resolve(downloadUrl);
                            })
                            .catch((error) => {
                                reject(error);
                            });

                    })
                    .catch((err) => {
                        console.log(err);
                    });

            })
            .catch((error) => {
                reject(error);
            });

    });
}

// Your remaining code below...
1 Like

Hi Pratham

Thank you SO MUCH. Everything is ok.
My three pbs have been resolved and the code works great.

Have a nice day

1 Like