Creating PDF from WIX content using Velo

Hello, like a few in the past, I have a need to create PDF documents from the content generated via Velo code (essentially, query results that are formatted in a certain way, like a summary of historical activity for a user under certain criteria).

Found an old post that was closed without resolution and then one with PDFgeneratorAPI (which now costs 60 USD per month!!!). Found pdfKit to be a better option (standard npm package), but can’t get it to work.

Here is what I have done so far:

  1. Added CreatePDF() as an event in events.js backend file

  2. Created pdfCreateor.jsw module in backend to call CreatePDF function

  3. CreatePDF code was kept just like it was in the old closed post (for simplicity). Here it is:

import PDFDocument from 'pdfkit';
import fs from 'fs-extra'; 

export async function CreatePDF()
{var pdf;
 var document = new PDFDocument();
 var writeStream = fs.createWriteStream('filename.pdf');
    document.pipe(writeStream);

    document.moveTo(300, 75)
        .lineTo(373, 301)
        .lineTo(181, 161)
        .lineTo(419, 161)
        .lineTo(227, 301)
        .fill('red', 'even-odd');  

 var loremIpsum = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam in...';  

    document.y = 320;
    document.fillColor('black')
    document.text(loremIpsum, {
        paragraphGap: 10,
        indent: 20,
        align: 'justify',
        columns: 2
    }); 
    document.end();
 
 var done = new Promise ( function (resolve, reject) {
        writeStream.on('finish', function () {
            resolve("write finished");
        });
        writeStream.on('error', function (error) {
            reject(error);
        });
    });
}

When I run this code in the backend using the RUN command (by the way, very useful one for backend code testing!), here is what I get as the message:

[backend/pdfCreator.jsw, CreatePDFjsw] called

[backend/pdfCreator.jsw, CreatePDFjsw] returned with undefined

I am expecting “write finished” as the message (resolve). Any hints/help?

Also, if anybody has had success with pdfKit, would they mind sharing their code and example output PDF files? Much appreciated!!

Hmmm I don’t have a full Velo solution (hoping someone else can provide it)

But I use Puppeteer to generate PDFs. Problem with this is, you need to build a web application hosted somewhere else, not on Wix & then you need to call that application to generate PDFs. You will not pay monthly fee to an API but there might be server costs wherever the application is hosted.

I haven’t used PDFKit, but I’ve used pdfmake & pdf-lib on Velo to create PDFs, which similarly use ArrayBuffers to store the PDF data. Of the two, PDF-lib was the easier to use of the two for me, and they of course are both open-sourced.

Unfortunately I can’t share the code, but I was able to pretty much adapt the examples from the pdf-lib examples for Velo , so I can guide you from there if that works.

Thank you @shantanukumar847 and @chris-derrell ! I’ll check these and post my experience and questions. Much appreciated!!

I am trying to use pdfmake as a replacement for pdfGeneratorAPI, creating a pdf in a backend jsw and then passing to the page code to display; I do not need to keep the pdf file. I don’t know how to take the pdfmake output and pass it back to the front end from the backend. Any chance you could provide a generic example of this step? Thanks in advance.

@davebogan98 Hey Dave! Sure. This was the code I used below in a file called pdf.js. The function createBuffer was exported to be used in another backend js file as **import** PDF **from** 'backend/document-tools/format/pdf.js' ;

Then for usage, it was used using await PDF . get ( jobId );

Note btw, this is not a generic example, so you’ll need to refer to the Pdfmake documentation to tweak it for your use case.

// backend/document-tools/format/pdf.js
import PdfPrinter from 'pdfmake';

const fonts = {
    Courier: {
        normal: 'Courier',
        bold: 'Courier-Bold',
        italics: 'Courier-Oblique',
        bolditalics: 'Courier-BoldOblique'
    },
    Helvetica: {
        normal: 'Helvetica',
        bold: 'Helvetica-Bold',
        italics: 'Helvetica-Oblique',
        bolditalics: 'Helvetica-BoldOblique'
    },
    Times: {
        normal: 'Times-Roman',
        bold: 'Times-Bold',
        italics: 'Times-Italic',
        bolditalics: 'Times-BoldItalic'
    },
    Symbol: {
        normal: 'Symbol'
    },
    ZapfDingbats: {
        normal: 'ZapfDingbats'
    }
};

const printer = new PdfPrinter(fonts);
// var fs = require('fs');

function createBuffer(id, filename, array = []) {
    let docDefinition = {
        defaultStyle: {
            font: 'Helvetica'
        },
        footer: {
            columns: [
                'Generated on myexamplewebsite.com'
            ]
        },
        content: [
            { text: filename, fontSize: 18, bold: true, lineHeight: 2 },
            ""
        ]
    };

    if (array) {
        // docDefinition.content = docDefinition.content.concat(array)
        docDefinition.content = docDefinition.content.concat(array.map(item => ({ text: item.text })));
    }

    let pdfDoc = printer.createPdfKitDocument(docDefinition);
    return new Promise((resolve, reject) => {
        try {
            var chunks = [];
            pdfDoc.on('data', chunk => chunks.push(chunk));
            pdfDoc.on('end', () => resolve(Buffer.concat(chunks)));
            pdfDoc.end();
        } catch (err) {
            reject(err);
        }
    });
}


export default {
    get: async ({ id, name, captions_array }) => {
        return await createBuffer(id, name, captions_array);
    }
}

This definitely keeps coming up and I have not had time to work on an example yet, but in trying to help another user I also found that a past forum post completed this with jsPDF that looked promising. I have not tested their solution, but something else to consider. Keep us posted on your solution! This seems like something we need a more formal tutorial for.

@chris-derrell Thanks for the quick reply, I’ll check this out and get back with any questions if I can’t figure something out. Appreciate it!

@chris-derrell Okay, just to make sure I understand… in this case, you are passing “jobid” to determine what kind of PDF to create, correct? You then return an array that contains the pieces of the generated pdf document. How do you then take that array and convert into something that can be displayed in a modal window or in some other way?

Also, the line:

docDefinition . content = docDefinition . content . concat ( array . map ( item => ({ text : item . text })));

throws a syntax editor when I copy it into the Editor; not sure if this is because of your use case or something I am not defining properly.

Sorry if I am being a bit dense – am not the most proficient JS programmer.

db

In my case jobId was a database row ID.

Looking back at the code, if you only have text content you’d like to put into it, you can skip the job ID and update the function to be along the lines of export asymc function createBuffer(textcontent). Then you can create the buffer there.

Also, please note all of this code runs in the backend files of Velo, because it’s expecting a node.js environment in order to work. In the browser, it’s running plain JavaScript and won’t work with pdfmake

@chris-derrell Got things to work, although now I have another problem that I will post separately. Thanks again for your help.

Glad to hear that you got it to work

@shantanukumar847 I had a meeting with https://www.browserless.io/ this morning regarding Puppeteer, and they could not solve why it was not working with Wix. Can you provide an example of how you set this up? I will have a video and my code to follow. I am desperate for an answer.

Here is my video and codes. Right now the urls are generic because I wanted anything to work also I am aware that it says screenshot right now, but browserless and Puppetteer can generate PDFS . My goal is to print each of these dynamic pages as pdfs and then query the database to get all of the “invoices”. I was using PDF Generator but this became cost prohibitive.

MY PAGE CODE

import { getPdfUrl } from ‘backend/createPDF.jsw’
export function printAll_click ( event ) {
console . log ( getPdfUrl ())
Promise . all ([ getPdfUrl ()]). then (( values ) => {
console . log ( values );
});
}

My JSW CODE

export async function getPdfUrl ( data ) {
( async () => {
const token = “API CODE from Browserless: Account” ;
const puppeteer = require ( ‘puppeteer’ );
const noApiKeyMessage = “You’re missing an API KEY” ;
const myBrowserlessAPIKey = token
console . log ( ‘Secret extracted to use browserless API KEY’ );
if (! myBrowserlessAPIKey )
return console . log ( noApiKeyMessage );
const browser = await puppeteer . connect ({ browserWSEndpoint : ‘wss://chrome.browserless.io?token=’ + myBrowserlessAPIKey });
const page = await browser . newPage ();
await page . goto ( ‘https://www.example.com’ ,
{ waitUntil : ‘networkidle2’ }
);
const title = await page . title ()
browser . close ();
return ( title )
})(). catch ( error =>{
console . log ( error );
});
}

here was one of the tutorials I used before getting in touch with browserless

Where is the files saved? I searched my media folder for ‘filename.pdf’ and it was not there.

Hi Chris, how did you get PDFs generated by pdf-lib to download?

1 Like