Node-html-pdf: A way to make toFile() synchronous ?

Created on 6 Sep 2016  路  14Comments  路  Source: marcbachmann/node-html-pdf

Hi everyone,

First of all, thanks a lot for this helpfull pdf generator.

I was wondering if there was any way to make pdf.create(...).toFile(...) synchronous (using await or a promise) instead of using the .toFile() callback ((err, res) => { ... }).

Regards,

F.

Most helpful comment

I converted it to a Node style promise - as per https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises

`const createPDF = (html, options) => new Promise(((resolve, reject) => {
        pdf.create(html, options).toBuffer((err, buffer) => {
            if (err !== null) {reject(err);}
            else {resolve(buffer);}
        });
    }));`

    const PDF = await createPDF(html, options);`

Using @web-ted 's example, I am unable to access the .toBuffer method. By wrapping the method - I can await it etc..

All 14 comments

This would require a rewrite of the library. For the time being you could try to promisify it using bluebird - http://bluebirdjs.com/docs/api/promise.promisify.html

It worked for me with:

var createResult = pdf.create(result, options);
var pdfToFile = Promise.promisify(createResult.__proto__.toFile, { context: createResult });
return pdfToFile();

After a look through the code, it looks as though including a "filename" field in "options" does the job.

const Promise = require('bluebird');
const pdf = Promise.promisifyAll(require('html-pdf'));
pdf.createAsync(html, { format: 'A4', filename: 'something.pdf' })
.then((pdf) => //something);

I can confirm that I works also with async/await

const Promise = require('bluebird');
const pdf = Promise.promisifyAll(require('html-pdf'));
async () => {
  let res = await pdf.createAsync(html, { format: 'A4', filename: 'something.pdf' });
  console.log(res.filename);
}

Adding to @tgropper's answer a usage example for a chained function, the returned value is the promise, so you should await it (or use .then)

const generateBill = async () => {
      let createResult = pdf.create(html, { format: 'A4' });
      let pdfToBuffer = Promise.promisify(createResult.__proto__.toBuffer, { context: createResult });
      let bufferResult = await pdfToBuffer();

      return bufferResult;
    }

    let bufferResult = await generateBill();

    res.set('Content-Type', 'application/pdf');
    res.send(bufferResult);

I tested something like that for es6/es7 and works:

async createFile(html, filename) {
        let options = {
            format: 'Letter',
            directory: os.tmpdir(),
            orientation: 'landscape',
            filename: 'test.pdf'
        };

        let create = util.promisify(pdf.create);
        let creator = await create(html, options);
    }

Node.js supports this now with utils native package.

I converted it to a Node style promise - as per https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises

`const createPDF = (html, options) => new Promise(((resolve, reject) => {
        pdf.create(html, options).toBuffer((err, buffer) => {
            if (err !== null) {reject(err);}
            else {resolve(buffer);}
        });
    }));`

    const PDF = await createPDF(html, options);`

Using @web-ted 's example, I am unable to access the .toBuffer method. By wrapping the method - I can await it etc..

I converted it to a Node style promise - as per https://stackoverflow.com/questions/22519784/how-do-i-convert-an-existing-callback-api-to-promises

`const createPDF = (html, options) => new Promise(((resolve, reject) => {
        pdf.create(html, options).toBuffer((err, buffer) => {
            if (err !== null) {reject(err);}
            else {resolve(buffer);}
        });
    }));`

    const PDF = await createPDF(html, options);`

Using @web-ted 's example, I unable to access the .toBuffer method. By wrapping the method - I can await it etc..

I think that you should be in a class for this to work.

Sorry - I don't understand your comment? Converting it to a promise does work. What benefit would a class have in this example?

Sorry - I don't understand your comment? Converting it to a promise does work. What benefit would a class have in this example?

Sorry for my poor response, but I meant that you need a context where you could use async / await instead of explicitly using a promise. Like:

const createPDF = async (html, options) => {}

The fact that you are later handling a stream might not be affected from the fact that you use a promise in a more elegant way. Don't have time to test it though.

I tested something like that for es6/es7 and works:

async createFile(html, filename) {
        let options = {
            format: 'Letter',
            directory: os.tmpdir(),
            orientation: 'landscape',
            filename: 'test.pdf'
        };

        let create = util.promisify(pdf.create);
        let creator = await create(html, options);
    }

Node.js supports this now with utils native package.

@web-ted I'm getting SIGABRT error. Any idea?

@BharathKumarRavichandran this usually happens when you are out of memory so your system kills the process. Since pdf creation is opening a browser headlessly you need a good amount of memory. Can you please check if this is the case ?

@tgoltsios Yeah I checked the kernel log, it didn't kill any process due to out of memory. Not sure why it's returning SIGABRT error. Also in the issues, I saw Phantomjs opening multiple processes without killing the old ones. See #506. Is this have anything to do with that?

@tgoltsios Yeah I checked the kernel log, it didn't kill any process due to out of memory. Not sure why it's returning SIGABRT error. Also in the issues, I saw Phantomjs opening multiple processes without killing the old ones. See #506. Is this have anything to do with that?

There is a good possibility that your heap size gets exhausted. Node.js is limiting memory to around 1.4Gb in newer versions and if you are using something more demanding than that, you may be limited by the platform. In that case you can increase memory by using something like that:

node --max_old_space_size=7168 index.js

Adjust that to your needs since it is quite large (7 Gb).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ascsi picture ascsi  路  3Comments

vivekiyer114 picture vivekiyer114  路  5Comments

hishamabutt picture hishamabutt  路  4Comments

cmoulliard picture cmoulliard  路  3Comments

antiframes picture antiframes  路  4Comments