3

I'm trying create custom footers such in phantomjs examples: https://github.com/ariya/phantomjs/blob/master/examples/printheaderfooter.js

Here is my code:

var phantom = require('node-phantom');

phantom.create(function (err, ph) {
    ph.createPage(function (err, page) {
         page.set('paperSize', {
              format: 'A4',
              orientation: 'portrait',
              footer: {
                contents: ph.callback(function (pageNum, numPages) {
                  if (pageNum == 1) {
                    return "";
                  }
                  return "<h1>Header <span style='float:right'>" + pageNum + " / " + numPages + "</span></h1>";
                })
              }
         }, function () {
             page.open('http://www.google.com', function () {
              })
         })
    })
});

But unfortunately I get the following error:

TypeError: Object #<Object> has no method 'callback';

Is it bug that ph does not expose callback method?

Matt Cain
  • 5,638
  • 3
  • 36
  • 45
Erik
  • 14,060
  • 49
  • 132
  • 218

4 Answers4

6

There are two problems in your script :

  • ph is not the classic phantom object, but a proxy object. node-phantom use web sockets to invoke phantomjs. Of course, some features are lost using this implementation.
  • functions are not serialized when calling page.set

Printing custom header/footer also requires to call phantom.callback. This method is not documented and so not exposed by node-phantom (and can't be). We need to find a way to apply this method in this package.

There are many solutions. Here is my possible solution :

Serialize your functions in a string in your script

var phantom = require('node-phantom');

phantom.create(function (err, ph) {
    ph.createPage(function (err, page) {
         page.set('paperSize', {
              format: 'A4',
              orientation: 'portrait',
              header: {
                            height: "1cm",
                            contents: 'function(pageNum, numPages) { return pageNum + "/" + numPages; }'
                        },
                        footer: {
                            height: "1cm",
                            contents: 'function(pageNum, numPages) { return pageNum + "/" + numPages; }'
                        }
         }, function () {   
             page.open('http://www.google.fr', function () {        
             page.render('google.pdf');
             ph.exit();
              })
         })
    })
});

edit bridge.js and add phantom.callback + eval. This allow us to re-plug the header/footer .contents.

case 'pageSet':
            eval('request[4].header.contents = phantom.callback('+request[4].header.contents+')');
            eval('request[4].footer.contents = phantom.callback('+request[4].footer.contents+')');
            page[request[3]]=request[4];
            respond([id,cmdId,'pageSetDone']);
            break;

As you can see this works ! (Google in French)

enter image description here

Ozair Kafray
  • 13,351
  • 8
  • 59
  • 84
Cybermaxs
  • 24,378
  • 8
  • 83
  • 112
  • Thanks. That works fine. I'll give you the bounty of course. May I ask you about ane question. What do you think about that bridge (I mean node-phantom) for phantomjs? Is it hack or good solusion for working with phantomjs in nodejs? – Erik Jun 17 '13 at 20:07
  • well the good question is : should we use phantomjs with node.js ? There is a good answer at http://stackoverflow.com/questions/15745394/can-phantomjs-work-with-node-js especially "Phantom and node have irreconcilable differences". Personnaly I'm not a big fan, but sometime we have to do bad things just to get the job done. – Cybermaxs Jun 17 '13 at 22:37
  • @Cybermaxs,its working great,how we can get total number of pages?http://stackoverflow.com/questions/23516294/node-phantom-show-number-of-generated-pdf-pages – Muhammad Rashid May 09 '14 at 11:38
  • If you're not satisfied with node-phantom you could also try phridge (https://github.com/peerigon/phridge). It provides a cleaner and more intuitive way to run scripts inside PhantomJS and return results back to node. – Johannes Ewald May 26 '14 at 14:26
  • Yep. That's just my humble opinion. If you don't like it, go ahead :) – Johannes Ewald May 26 '14 at 14:43
1

Unfortunately, node-phantom doesn't appear to support phantom.callback. Since the project is inactive for more than a year, I think it's unlikely to be updated in the near future.

On the other hand, phantomjs-node supports phantom.callback() since version 0.6.6. You can use it like this:

var phantom = require('phantom');

phantom.create(function (ph) {
    ph.createPage(function (page) {
        page.open("http://www.google.com", function (status) {

            var paperConfig = {
                format: 'A4',
                orientation: 'portrait',
                border: '1cm',
                header: {
                    height: '1cm',
                    contents: ph.callback(function(pageNum, numPages) {
                        return '<h1>My Custom Header</h1>';
                    })
                },
                footer: {
                    height: '1cm',
                    contents: ph.callback(function(pageNum, numPages) {
                        return '<p>Page ' + pageNum + ' / ' + numPages + '</p>';
                    })
                }
            };

            page.set('paperSize', paperConfig, function() {
                // render to pdf
                page.render('path/to/file.pdf', function() {
                    page.close();
                    ph.exit();
                });
            });
        });
    });
});

As you can also see on this gist.

0

node phantom seems to expose this proxy-object via the create function (this should be your ph-object):

var proxy={
                createPage:function(callback){
                    request(socket,[0,'createPage'],callbackOrDummy(callback));
                },
                injectJs:function(filename,callback){
                    request(socket,[0,'injectJs',filename],callbackOrDummy(callback));
                },
                addCookie: function(cookie, callback){
                    request(socket,[0,'addCookie', cookie],callbackOrDummy(callback));
                },
                exit:function(callback){
                    request(socket,[0,'exit'],callbackOrDummy(callback));
                },
                on: function(){
                    phantom.on.apply(phantom, arguments);
                },
                _phantom: phantom
            };

that means, that you can probably acces the phantoms callback like this:

ph._phantom.callback
hereandnow78
  • 14,094
  • 8
  • 42
  • 48
0

Here what I did to access phantom.callback:

add this to node-phantom.js line 202:

callback: function(callback){
  request(socket,[0,'callback'],callbackOrDummy(callback));
},

just before _phantom: phantom

and add this to bridge.js line 45:

case 'callback':
    phantom.callback(request[3]);
break;

Hope it helps!

rafaelcastrocouto
  • 11,781
  • 3
  • 38
  • 63
  • Does this solution works for you? I've added the code above and now I don't get the error but I don't get a footer as well. Are you sure that it's enough? – Erik Jun 17 '13 at 18:57
  • I just show'd you how to access the phantom.callback object, not all the solution ... "well the good question is : should we use phantomjs with node.js ?" awesome question #Cybermaxs - Betclic – rafaelcastrocouto Jun 18 '13 at 22:23