cesarvr / pdf-generator

Cordova plugin to generate pdf in the client-side
MIT License
107 stars 61 forks source link

Unable to generate multiple base64 files #103

Closed lukawal-miller closed 4 years ago

lukawal-miller commented 4 years ago

I am trying to generate multiple base64 files but have no luck.

let data = ['<h1>Test 1</h1>', '<h1>Test 2</h1>', '<h1>Test 3</h1>']

generatePDF(data) {
 return Promise(function (resolve) {
  pdf.fromData(data, {type: 'base64'}).then(function(base64) {
   resolve(base64);
  }
 });
}

let base64Array = [];
for (let i = 0; i < data.length; i++) {
 base64Array.push(this.generatePDF(data[i]));
};

Promise.all([base64Array]).then(function(values) {
  console.log(values);
});

Expected output should be an Array: [base64, base64, base64] But it looks like it stuck after first Promise and never completes.

Anyone can help me to find a solution to this?

cesarvr commented 4 years ago

Was checking the plugin at you are correct, it doesn't work. And this is because the backend (the code that generates the PDF using Webkit) is working as a singleton, basically the way your are using the plugin to make concurrent drawing request is not supported, sorry :(.

Solution 1: you can make a print queue, basically you need to go one by one, take first member, transform into pdf, get base64, then you take the second, transform into pdf and so on.

Solution 2: Modify the plugin to support concurrent HTML to PDF transformation, it will require make a new Thread per Webkit per call, at the moment I don't have enough time to dedicate to this project.

But if you feel strong, I'll be glad to incorporate your contributions.

lukawal-miller commented 4 years ago

Thanks for your reply. Unfortunately I do not have time and enough knowledge to modify the plugin and solution 1 is not what I'm looking for. Hoping that someone else might update your plugin in near future.

bengrah-miller commented 4 years ago

Hi @cesarvr - I work with @lukawal-miller and wanted to pick this thread up. Can I ask for more info about what you mean by print queue?

In our code when we call the fromData() function multiple times, it succeeds on the first call but fails on the second, so I'm not sure how your proposed solution would work? Any steer you could give me here would be much appreciated!

bengrah

cesarvr commented 4 years ago

Hi @lukawal-miller , basically the backend (which is singleton WebKit) requires time to finish the transformation of HTML to PDF, because of its singleton nature, if you pass multiple URL's and don't allow no enough time to finish rendering then it will only render the last URL. If you use something like Promise.all it will fail.

What I suggest is that you basically queue the work manually, you pick one element transform it get your base64, then go to the next and so on.

You can chain promises like in this example:

let urls = ['url1', 'url2']

pdf.fromURL({url: urls[0]})
.then(base64 => doSomethingWithIt(base64))
.then(() =>{

 return pdf.fromURL({url:urls[1]})
})
.then(base64 => /* and so on..*/  ) 

Cheers, César.

bengrah-miller commented 4 years ago

Hi @cesarvr

Thanks for that - I think you're operating under the idea we know exactly how many PDFs we're going to generate each time.

Our use case is, the user selects from a checklist what kind of items they want to generate. For example, the user has a list of trades to choose from - they tick which trade that want to generate a PDF for:

Plumber [ ] Joiner [x] Roofer [x] Cleaner [x] Gardener [ ]

In our example, we've ticked three items, but it could be 4, 5, 1 etc. From what I can see in your example, you'd have to know exactly in the code how many you're going to do each time - would that be correct?

bengrah

cesarvr commented 4 years ago

Well I just wanted to give you an idea of how to solve the problem and overcome the plugin limitation, if your case evolves around a dynamic array then you could write an recurrent algorithm similar to this:


function makePDFRecursively(URLs){
 if( URLs.length > 0 ) {
   return false
 }
  return pdf.fromURL({url: URLs.pop()})
   .then(base64 => doSomethingWithIt(base64))
   .then(() =>{
 return makePDFRecursively(URLs)
})

let urls = ['url1', 'url2']

makePDFRecursively(urls)
lukawal-miller commented 4 years ago

Hi @cesarvr

Thanks for your help on this one. Thanks to your suggestions we were able to resolve our issue by using the following code:

async function generateMultiplePDF() {
    let htmlArray = ['<h1>Hello 1</h1>', '<h1>Hello 2</h1>'];
    let base64Array = [];

    let options = {
        documentSize: 'A4',
        type: 'base64'
    }

    for (let i = 0; i < htmlArray.length; i++) {
        await pdf.fromData(htmlArray[i], options).then((base64) => {
            base64Array.push(base64)
        });
    }

    return base64Array;
}

Thanks again for your help and this plugin!