Generate a PDF out of an HTML template

I need help to render a PDF out of a page I’m generating.

I am a rails developer and for this task I usually use a gem called wicked_pdf.

Now I’m trying to obtain the same in result in Meteor, which I am loving so far. I tried ongoworks:pdf but it doesn’t work with styling.

Can you help me? I’m kinda new in Meteor

NB:
I am using FlowRouter

This is my package:

....(default packages)...

meteorhacks:flow-layout
meteorhacks:flow-router
mrt:bootstrap-3
fortawesome:fontawesome
matb33:collection-hooks
dburles:collection-helpers
momentjs:moment

matdutour:popup-confirm

copleykj:livestamp

udondan:bulk-collection-update

ongoworks:pdf

Maybe this will help…

Is there any way to do it from the client-side?

Yes, for client-side pdf rendering you could use e.g. https://atmospherejs.com/pascoual/pdfjs.

As for the error. Did you define filePath? Show me your code.

This worked great for me https://themeteorchef.com/tutorials/rendering-pdfs-with-react-components

Hey Tom I get this error:

I20170110-09:51:59.985(1)? Error phantomjs:
I20170110-09:51:59.997(1)? { [Error: Command failed: /bin/sh -c phantomjs assets/app/phantomDriver.js /tmp/menuTue Jan 10 2017 09:51:59 GMT+0100 (CET).html
I20170110-09:51:59.998(1)? /bin/sh: -c: line 0: syntax error near unexpected token `('
I20170110-09:51:59.998(1)? /bin/sh: -c: line 0: `phantomjs assets/app/phantomDriver.js /tmp/menuTue Jan 10 2017 09:51:59 GMT+0100 (CET).html'
I20170110-09:51:59.998(1)? ]
I20170110-09:51:59.998(1)?   killed: false,
I20170110-09:51:59.998(1)?   code: 2,
I20170110-09:51:59.999(1)?   signal: null,
I20170110-09:51:59.999(1)?   cmd: '/bin/sh -c phantomjs assets/app/phantomDriver.js /tmp/menuTue Jan 10 2017 09:51:59 GMT+0100 (CET).html' }
I20170110-09:52:00.051(1)? Exception while invoking method 'render.pdf' ReferenceError: email is not defined
I20170110-09:52:00.051(1)?     at [object Object].renderPdf (server/menu.js:94:5)
I20170110-09:52:00.051(1)?     at maybeAuditArgumentChecks (packages/ddp-server/livedata_server.js:1711:12)
I20170110-09:52:00.051(1)?     at packages/ddp-server/livedata_server.js:711:19
I20170110-09:52:00.052(1)?     at [object Object]._.extend.withValue (packages/meteor.js:1122:17)
I20170110-09:52:00.052(1)?     at packages/ddp-server/livedata_server.js:709:40
I20170110-09:52:00.052(1)?     at [object Object]._.extend.withValue (packages/meteor.js:1122:17)
I20170110-09:52:00.053(1)?     at packages/ddp-server/livedata_server.js:707:46
I20170110-09:52:00.053(1)?     at Session.method (packages/ddp-server/livedata_server.js:681:23)
I20170110-09:52:00.053(1)?     at packages/ddp-server/livedata_server.js:551:43

The only thing I changed from your tutorial is that I use:

let html = SSR.compileTemplate('menuPrivate', Assets.getText('menuPrivate.html'));

Do you have a date object in your htmls filename? tmp/menuTue Jan 10 2017 09:51:59 GMT+0100 (CET).html

Shame on me sorry I’m removing it :slight_smile: Is there a way to browse the pdf file without emailing it?

Now everything seems to work but from what i can see in the server console it just get stucked during the job:

console:

I20170110-10:28:43.113(1)? ######################################### 1
I20170110-10:28:43.150(1)? ######################################### 2
I20170110-10:28:43.150(1)? ######################################### 3 define file name
I20170110-10:28:43.150(1)? ######################################### 4 saved
I20170110-10:28:43.151(1)? ######################################### 5 saved
I20170110-10:28:43.151(1)? ######################################### /tmp/menu.html
I20170110-10:28:43.151(1)? ######################################### 6
I20170110-10:28:43.151(1)? ######################################### 7
I20170110-10:28:43.151(1)? ######################################### 8

Code:

  'render.pdf': function(menu){
    console.log('######################################### 1')
    let html = SSR.compileTemplate('menuPrivate', Assets.getText('menuPrivate.html'));
    console.log('######################################### 2')
    // generate file name and path
    let filePath;
    let filename = `menu.html`;
    console.log('######################################### 3 define file name')
    if ( process.env.NODE_ENV === 'development' ) {
      filePath = '/tmp/' + filename;
    } else {
      let path = Npm.require('path');
      filePath = path.join( process.env.TEMP_DIR, filename );
    }

    console.log('######################################### 4 saved')

    // write html to file
    let fs = Npm.require('fs');
    let writeFileSync = Meteor.wrapAsync( fs.writeFile );
    try {
      writeFileSync( filePath, html );
    } catch ( error ) {
      console.log( 'Error writing html to file:');
      console.log( error );
    }

    console.log('######################################### 5 saved')
    console.log('#########################################', filePath)
    // call phantom to render pdf from html
    let childProcess = Npm.require('child_process');
    console.log('######################################### 6')
    let cmd = 'phantomjs assets/app/phantom-driver.js ' + filePath;
    console.log('######################################### 7')
    let execSync = Meteor.wrapAsync( childProcess.exec );
    console.log('######################################### 8')    
    try {
      execSync( cmd );
      console.log('######################################### 9 cmd');
    } catch ( error ) {
      console.log( 'Error phantomjs:');
      console.log( error );
    }

  },

and it now start as I start the server, it doesn’t wait for the method to be called

I made some changes and used your Spacebars.toHTML but it still get stucked and it’s like the job remain suspended. But nothing works and I have no log in the console. Everything is blocked at this line:

execSync( cmd );

here the complete method:

'render.pdf': (menu) => {
    console.log('######################################### 1');
    let dataContext = {
      categories: () => {
        Category.find({}, {sort: {categoryOrder: 1}});
      }
    }

    // let html = SSR.compileTemplate('menuPrivate', Assets.getText('menuPrivate.html'));
    let html = Spacebars.toHTML( dataContext, Assets.getText('menuPrivate.html') );
    console.log('######################################### 2')
    // generate file name and path
    let filePath;
    let filename = `menu.html`;
    console.log('######################################### 3 define file name')
    if ( process.env.NODE_ENV === 'development' ) {
      filePath = '/tmp/' + filename;
    } else {
      let path = Npm.require('path');
      filePath = path.join( process.env.TEMP_DIR, filename );
    }

    console.log('######################################### 4 saved')

    // write html to file
    let fs = Npm.require('fs');
    let writeFileSync = Meteor.wrapAsync( fs.writeFile );
    try {
      writeFileSync( filePath, html );
    } catch ( error ) {
      console.log( 'Error writing html to file:');
      console.log( error );
    }

    console.log('######################################### 5 saved')
    console.log('#########################################', filePath)
    // call phantom to render pdf from html
    let childProcess = Npm.require('child_process');
    console.log('######################################### 6')
    let cmd = 'phantomjs assets/app/phantom-driver.js ' + filePath;
    console.log('######################################### 7')
    let execSync = Meteor.wrapAsync( childProcess.exec );
    console.log('######################################### 8', cmd)    
    console.log('######################################### 8', execSync)    
    try {
      execSync( cmd );
      console.log('######################################### 9 cmd');
    } catch ( error ) {
      console.log( 'Error phantomjs:');
      console.log( error );
    }



    // // attach pdf to email
    //   console.log('######################################### 10');
    
    // email.attachments = [{
    //   fileName: `menu.pdf`,
    //   filePath: filePath.replace('.html', '.pdf'),
    // }];

    //   console.log('######################################### 11 cmd');
    

    // // and send the email
    // Email.send( email );

  },

did you install phantom on your system? what is the output in the console?

did you already try to run the phantomscript in isolation, i.e. not call it from meteor but just from the command line? does that work?

I installed phantomjs and this is the location:

$ which phantomjs
/usr/local/bin/phantomjs
$ phantomjs --version
2.1.1

Creating a js file and run it with phantomjs command run smoothly with the offical examples

I managed to make it work. Now the only problem is that it doesn’t print the contents. I’m almost there :smiley: :heart_eyes:

(for the problem above i just reinstalled phantomjs from NPM)

1 Like

I opened a separate thread for the point where I am stuck at (just in case someone wants to help me or follow up the discussion)

https://forums.meteor.com/t/access-template-parentdata-0–id-in-a-server-side-rendered-html/32981

Dr. Spiesser, I trust that ALL is well. I’d like to dialog with you about Meteor-to-PDF, if I may.

Can the PDF generated actually be an intelligent PDF, that can do the following:

  1. create and email a PDF that holds the input data right in the PDF
  2. allow for edits made in the subsequent PDF to update the data store

Thanks for your time in responding. My direct email is self@rafikicai.com

What happens if the PDF is sent to two different people? Which one, when edited, is the correct data input source?

In the instance that multiple parties are authorized recipients, then I
would
reason that each of those parties are empowered with editing rights.

Interestingly enough, although adding a bit of complexity, the document
could be
area mapped; whereby one recipient is empowered to edit certain regions and
another is empowered to edit other regions.

Or more simply: even with multiple recipients, only some recipients could
be
empowered to edit; visa vi either a password, or key micro-client or
blockchain
style with a certain key.

My thoughts off the cuff.

I don’t think this answer addresses the heart of the question.

What happens when two people are given access to the same area? Not necessarily by you. You know how humans are. They will share the password with each other if they feel it makes their life easier.