Update multiple pdf template using data from user input

There is another post here: Generate and download a custom PDF with pdf-lib where she says she got this working but I am new at this and don’t know where or how to use the code. Or are there other examples.

Well, like i already mentioned, there are various solutions available regarding your issue.

1) You could use for example NPM-Packages like;

a) print-js —> print-js - npm
This is not directly a library for PDF-Generation, but possible to generate a PDF-File anyway.
b) PDF-Lib —> https://pdf-lib.js.org/
c) PDF-Generator —> Example: PDF Generator
PDF-Generator → PDF GENERATOR API npm demo
d) another solution here ? → PDF file generation using pdfmake
e) and many more

  1. You could also use extern services like …
    https://pdfmyurl.com/
    https://pdflayer.com/

  2. Or you generate your own solution, for example…

Example-1: Using the PRINT-JS + Wix-HTML-Component (created by russian-dima)

This solutions requires an HTML-Component to be added onto your page. To be able to use this solution fully → you will need a PREMIUM-Wix-Account.

This simple solution will use → PRINT-JS-LIBRARY (mentioned before in the NPM-Packages)

<!DOCTYPE html>
<html>
<head>
  <title>Print JS Example</title>
  <script src="https://printjs-4de6.kxcdn.com/print.min.js"></script>
  <style>  
  </style>
</head>
<body>
  <div class="container">
    <div id="printable-content"></div>
    <button onclick="printContent()">Print Content</button>
  </div>

   <script>
     function generateHtmlFromJson(data) {
     		let htmlContent = '';

        if (Array.isArray(data)) {
          data.forEach(item => {
            htmlContent += generateHtmlFromJson(item);
          });
        } else if (typeof data === 'object') {
          htmlContent += '<div class="item">';
          for (const key in data) {
            if (Array.isArray(data[key])) {
              htmlContent += `<div class="item-title">${key}:</div>`;
              htmlContent += '<ul>';
              data[key].forEach(subItem => {
                htmlContent += '<li>' + generateHtmlFromJson(subItem) + '</li>';
              });
              htmlContent += '</ul>';
            } else if (typeof data[key] === 'object') {
              htmlContent += `<div class="item-title">${key}:</div>`;
              htmlContent += generateHtmlFromJson(data[key]);
            } else {
              htmlContent += `<div class="item-detail"><strong>${key}:</strong> ${data[key]}</div>`;
            }
          }
          htmlContent += '</div>';
        }

        return htmlContent;
      }

      function renderContent() {
        // Generate HTML from JSON data
        let htmlContent = generateHtmlFromJson(jsonData);

        // Inject the generated HTML into the printable content div
        const printableContent = document.getElementById('printable-content');
        printableContent.innerHTML = htmlContent;
        printableContent.style.display = 'block';
      }

      function printContent() {
        // Use print-js to print the content
        printJS({
          printable: 'printable-content',
          type: 'html',
          targetStyles: ['*']
        });
      }

      // Call renderContent on page load
      window.onload = renderContent;
    </script>
  </body>
</html>

Let us imagine we have generated some kind of DATA out of our DATABASE, which looks for example like the following…

const jsonData = [
      {
        "productID": "1001",
        "productName": "Wireless Mouse",
        "productCategory": "Electronics",
        "productPrice": 29.99,
        "productStock": 100,
        "productDescription": "A high-precision wireless mouse with ergonomic design."
      },
      {
        "productID": "1002",
        "productName": "Bluetooth Headphones",
        "productCategory": "Electronics",
        "productPrice": 59.99,
        "productStock": 50,
        "productDescription": "Noise-cancelling over-ear Bluetooth headphones with long battery life."
      },
      {
        "productID": "1003",
        "productName": "Yoga Mat",
        "productCategory": "Fitness",
        "productPrice": 19.99,
        "productStock": 200,
        "productDescription": "Eco-friendly non-slip yoga mat for all types of yoga and exercise."
      },
      {
        "productID": "1004",
        "productName": "Water Bottle",
        "productCategory": "Fitness",
        "productPrice": 14.99,
        "productStock": 150,
        "productDescription": "Stainless steel insulated water bottle keeps drinks hot or cold for hours."
      },
      {
        "productID": "1005",
        "productName": "Desk Organizer",
        "productCategory": "Office Supplies",
        "productPrice": 24.99,
        "productStock": 75,
        "productDescription": "Multi-functional desk organizer with compartments for pens, paper, and gadgets."
      }
    ];

…and now we wnt to either print it out, or generate a PDF-File out of this data.

Now our HTML-Component comes into place.
We are using the shown code inside of the HTML.Component…
And we get something like …

At first look, there is not much to see → just a PRINT-Button.
That is right, because we did not add our DATA into it.

Let’s do it…

<!DOCTYPE html>
<html>
<head>
  <title>Print JS Example</title>
  <script src="https://printjs-4de6.kxcdn.com/print.min.js"></script>
  <style>  
  </style>
</head>
<body>
  <div class="container">
    <div id="printable-content"></div>
    <button onclick="printContent()">Print Content</button>
  </div>

   <script>
   	const jsonData = [
      {
        "productID": "1001",
        "productName": "Wireless Mouse",
        "productCategory": "Electronics",
        "productPrice": 29.99,
        "productStock": 100,
        "productDescription": "A high-precision wireless mouse with ergonomic design."
      },
      {
        "productID": "1002",
        "productName": "Bluetooth Headphones",
        "productCategory": "Electronics",
        "productPrice": 59.99,
        "productStock": 50,
        "productDescription": "Noise-cancelling over-ear Bluetooth headphones with long battery life."
      },
      {
        "productID": "1003",
        "productName": "Yoga Mat",
        "productCategory": "Fitness",
        "productPrice": 19.99,
        "productStock": 200,
        "productDescription": "Eco-friendly non-slip yoga mat for all types of yoga and exercise."
      },
      {
        "productID": "1004",
        "productName": "Water Bottle",
        "productCategory": "Fitness",
        "productPrice": 14.99,
        "productStock": 150,
        "productDescription": "Stainless steel insulated water bottle keeps drinks hot or cold for hours."
      },
      {
        "productID": "1005",
        "productName": "Desk Organizer",
        "productCategory": "Office Supplies",
        "productPrice": 24.99,
        "productStock": 75,
        "productDescription": "Multi-functional desk organizer with compartments for pens, paper, and gadgets."
      }
    ];
    
     function generateHtmlFromJson(data) {
     		let htmlContent = '';

        if (Array.isArray(data)) {
          data.forEach(item => {
            htmlContent += generateHtmlFromJson(item);
          });
        } else if (typeof data === 'object') {
          htmlContent += '<div class="item">';
          for (const key in data) {
            if (Array.isArray(data[key])) {
              htmlContent += `<div class="item-title">${key}:</div>`;
              htmlContent += '<ul>';
              data[key].forEach(subItem => {
                htmlContent += '<li>' + generateHtmlFromJson(subItem) + '</li>';
              });
              htmlContent += '</ul>';
            } else if (typeof data[key] === 'object') {
              htmlContent += `<div class="item-title">${key}:</div>`;
              htmlContent += generateHtmlFromJson(data[key]);
            } else {
              htmlContent += `<div class="item-detail"><strong>${key}:</strong> ${data[key]}</div>`;
            }
          }
          htmlContent += '</div>';
        }

        return htmlContent;
      }

      function renderContent() {
        // Generate HTML from JSON data
        let htmlContent = generateHtmlFromJson(jsonData);

        // Inject the generated HTML into the printable content div
        const printableContent = document.getElementById('printable-content');
        printableContent.innerHTML = htmlContent;
        printableContent.style.display = 'block';
      }

      function printContent() {
        // Use print-js to print the content
        printJS({
          printable: 'printable-content',
          type: 'html',
          targetStyles: ['*']
        });
      }

      // Call renderContent on page load
      window.onload = renderContent;
    </script>
  </body>
</html>

As result we get the following on our screen…

This already looks promising. We can see our DATA which we got earlier from our DATABASE inside the HTML-Component and we can even print it out.
But what else we cen do, right now?

YES → we can even generate a PDF-File out of it, because you have the option for it inside of the PRINT-MENU …

Opening the generated PDF-File, will show you exactly the content you have created earlier…
2024-07-26 17_07_01-Window

And you can open the PDF-file with any PDF-compatible tool of your choice…

But you do not have to stop at this point, because in this example, there is still no option given to get the data AUTOMATICALLY from your DATABASE, here you still have to put it in → Manually.

That means you will have first to establish a CONNECTION between HTML-Component and your Wix-Page (your data will be send from your page to HTML-Comp.), to be able to let this work automatically.

Another point is the → VISUALIZATION ← of your PDF.

Adding some additional CODE or STYLES, will make your solution more user-friendly and your generated document more readable.

How to achieve this?
Ok, let us add some more → CSS-CODING ← into our little APP/Tool…

<!DOCTYPE html>
<html>
<head>
  <title>Print JS Example</title>
  <script src="https://printjs-4de6.kxcdn.com/print.min.js"></script>
  <style>  
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
      background-color: #f4f4f4;
    }
    .container {
      max-width: 800px;
      margin: 0 auto;
      background-color: #fff;
      padding: 20px;
      box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
    }
    .item {
      margin-bottom: 20px;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 5px;
      background-color: #f9f9f9;
    }
    .item-title {
      font-size: 22px;
      font-weight: bold;
      color: #333;
      margin-bottom: 10px;
    }
    .item-detail {
      font-size: 16px;
      margin-left: 20px;
      color: #555;
    }
    button {
      display: block;
      width: 100%;
      padding: 10px;
      font-size: 18px;
      color: #fff;
      background-color: #007BFF;
      border: none;
      border-radius: 5px;
      cursor: pointer;
      margin-top: 20px;
    }
    button:hover {
      background-color: #0056b3;
    }
  </style>
</head>
<body>
  <div class="container">
    <div id="printable-content"></div>
    <button onclick="printContent()">Print Content</button>
  </div>

   <script>
   	const jsonData = [
      {
        "productID": "1001",
        "productName": "Wireless Mouse",
        "productCategory": "Electronics",
        "productPrice": 29.99,
        "productStock": 100,
        "productDescription": "A high-precision wireless mouse with ergonomic design."
      },
      {
        "productID": "1002",
        "productName": "Bluetooth Headphones",
        "productCategory": "Electronics",
        "productPrice": 59.99,
        "productStock": 50,
        "productDescription": "Noise-cancelling over-ear Bluetooth headphones with long battery life."
      },
      {
        "productID": "1003",
        "productName": "Yoga Mat",
        "productCategory": "Fitness",
        "productPrice": 19.99,
        "productStock": 200,
        "productDescription": "Eco-friendly non-slip yoga mat for all types of yoga and exercise."
      },
      {
        "productID": "1004",
        "productName": "Water Bottle",
        "productCategory": "Fitness",
        "productPrice": 14.99,
        "productStock": 150,
        "productDescription": "Stainless steel insulated water bottle keeps drinks hot or cold for hours."
      },
      {
        "productID": "1005",
        "productName": "Desk Organizer",
        "productCategory": "Office Supplies",
        "productPrice": 24.99,
        "productStock": 75,
        "productDescription": "Multi-functional desk organizer with compartments for pens, paper, and gadgets."
      }
    ];
    
     function generateHtmlFromJson(data) {
     		let htmlContent = '';

        if (Array.isArray(data)) {
          data.forEach(item => {
            htmlContent += generateHtmlFromJson(item);
          });
        } else if (typeof data === 'object') {
          htmlContent += '<div class="item">';
          for (const key in data) {
            if (Array.isArray(data[key])) {
              htmlContent += `<div class="item-title">${key}:</div>`;
              htmlContent += '<ul>';
              data[key].forEach(subItem => {
                htmlContent += '<li>' + generateHtmlFromJson(subItem) + '</li>';
              });
              htmlContent += '</ul>';
            } else if (typeof data[key] === 'object') {
              htmlContent += `<div class="item-title">${key}:</div>`;
              htmlContent += generateHtmlFromJson(data[key]);
            } else {
              htmlContent += `<div class="item-detail"><strong>${key}:</strong> ${data[key]}</div>`;
            }
          }
          htmlContent += '</div>';
        }

        return htmlContent;
      }

      function renderContent() {
        // Generate HTML from JSON data
        let htmlContent = generateHtmlFromJson(jsonData);

        // Inject the generated HTML into the printable content div
        const printableContent = document.getElementById('printable-content');
        printableContent.innerHTML = htmlContent;
        printableContent.style.display = 'block';
      }

      function printContent() {
        // Use print-js to print the content
        printJS({
          printable: 'printable-content',
          type: 'html',
          targetStyles: ['*']
        });
      }

      // Call renderContent on page load
      window.onload = renderContent;
    </script>
  </body>
</html>

Now, we will get this, regarding the → VISUALIZATION <---- of your tool…

You can add new…

  1. OPTIONS
  2. FUNCTIONS
  3. FEATURES
  4. DESIGNS
  5. STRUCTURE
  6. WHATEVER

…into your own generated solution driven by PRINT-JS-LIBRARY.

But as i told you → THERE ARE SO MANY DIFFERENT SOLUTIONS FOR THIS.

The good thing on this solution is:

  • you already have the print-option included.
  • you can customize it → like you want.
  • you can expand it’s functionalities and features like you want.
  • you can combine it with other solutions or tools.
  • you can connect it to your wix-page & database.

Thank you so much for all of this. My solution I am trying to use is the pdf-lib and I almost have it working but when I click the button to generate the pdf it tells me in the console that my savePDF function in my backend file is not a function. No clue why.
Here is the code from my backend file if you can make sense of the error.

//Filename: handlePDF.web.js
import { Permissions, webMethod } from "wix-web-module";

export const multiply = webMethod(Permissions.Anyone, (a, b) => a * b);

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);
                    })
                    .catch((error) => {
                        reject(error);
                    });
            })
            .catch((error) => {
                reject(error);
            });
    });
}

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

    return mediaManager.upload(
        "/UserUploads",
        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, 10);
    return myFileDownloadUrl;
}

Look at the imports…

import { savePDF } from "backend/handlePDF";
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
import wixLocation from 'wix-location';

What is missing in your code?

By the way → IMPORTS → ALWAYS ← goes to the very first lines of code.

Maybe you mixed something up!
Go back to the well described tutorial → and read it again nad again and again.
This way → you will find your solution. All you have to do is almost just COPY and PASTE, without to do to much editings.