Skip to content

Commit

Permalink
Merge pull request #15 from bjrmatos/master
Browse files Browse the repository at this point in the history
support waitForJS execution in phantom web page (callback based)
  • Loading branch information
pofider committed Oct 15, 2015
2 parents 021ee5e + 5730fff commit adf62c7
Show file tree
Hide file tree
Showing 8 changed files with 175 additions and 8 deletions.
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ conversion({
footer: "<h2>foo</h2>",
url: "http://jsreport.net",//set direct url instead of html
printDelay: 0,//time in ms to wait before printing into pdf
waitForJS: true,//set to true to enable programmatically specify (via Javascript of the page) when the pdf printing starts (see Programmatic pdf printing section for an example)
waitForJSVarName: //name of the variable that will be used as a printing trigger, defaults to "PHANTOM_HTML_TO_PDF_READY" (see Programmatic pdf printing section for an example)
allowLocalFilesAccess: false,//set to true to allow request starting with file:///
paperSize: {
format, orientation, margin, width, height, headerHeight, footerHeight
Expand Down Expand Up @@ -72,6 +74,38 @@ conversion.kill();
##Page numbers
Use directives `{#pageNum}` and `{#numPages}` inside header or footer to add current page number resp. total number of pages.

##Programmatic pdf printing
If you need to programmatic trigger the pdf printing process (because you need to calculate some values or do something async in your page before printing) you can enable the `waitForJS` local option, when `waitForJS` is set to true the pdf printing will wait until you set a variable to true in your page, by default the name of the variable is `PHANTOM_HTML_TO_PDF_READY` but you can customize it via `waitForJSVarName` option.

**Example:**

local options:
```js
conversion({
html: "<custom html here>",
waitForJS: true,
viewportSize: {
width: 600,
height: 600
},
format: {
quality: 100
}
}, cb);
```

custom html:
```html
<h1></h1>
<script>
// do some calculations or something async
setTimeout(function() {
window.PHANTOM_HTML_TO_PDF_READY = true; //this will start the pdf printing
}, 500);
</script>
```


##Further notes
You may find some further information and usage examples in the [jsreport documentation](http://jsreport.net/learn/phantom-pdf) or try pdf printing in the [online playground](https://playground.jsreport.net/#/playground/xykdJcxR5).

Expand Down
5 changes: 5 additions & 0 deletions lib/conversion.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ function convert(opt, cb) {
opt.viewportSize = opt.viewportSize || {};
opt.paperSize = opt.paperSize || {};
opt.settings = opt.settings || {};
opt.waitForJSVarName = opt.waitForJSVarName || 'PHANTOM_HTML_TO_PDF_READY';

if (opt.waitForJS && opt.settings.javascriptEnabled === false) {
throw new Error('can\'t use waitForJS option if settings.javascriptEnabled is not activated');
}

var id = uuid();

Expand Down
7 changes: 6 additions & 1 deletion lib/dedicatedProcessStrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,16 @@ module.exports = function(options, requestOptions, id, cb) {
});

setTimeout(function() {
var timeoutErr;

if (isDone)
return;

isDone = true;
cb(new Error("Timeout when executing in phantom"));
timeoutErr = new Error("Timeout when executing in phantom");
timeoutErr.phantomTimeout = true;

cb(timeoutErr);
}, requestOptions.timeout || options.timeout);
});
};
42 changes: 40 additions & 2 deletions lib/scripts/conversionScriptPart.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
var pageJSisDone = body.waitForJS ? false : true;

page.viewportSize = {
width: body.viewportSize.width || 600,
height: body.viewportSize.height || 600
Expand All @@ -21,6 +23,32 @@ page.onResourceRequested = function (request, networkRequest) {
if (request.url.lastIndexOf("file://", 0) === 0 && request.url.lastIndexOf("file:///", 0) !== 0) {
networkRequest.changeUrl(request.url.replace("file://", "http://"));
}

if (body.waitForJS && request.url.lastIndexOf("http://intruct-javascript-ending", 0) === 0) {
pageJSisDone = true;
}
};

page.onInitialized = function() {
// inject function to the page in order to the client can instruct the ending of its JS
if (body.waitForJS) {
page.evaluate(function(varName) {
if (typeof Object.defineProperty === 'function') {
Object.defineProperty(window, varName, {
set: function(val) {
if (!val)
return;

if (val === true) {
var scriptNode = document.createElement("script");
scriptNode.src = 'http://intruct-javascript-ending';
document.body.appendChild(scriptNode);
}
}
});
}
}, body.waitForJSVarName);
}
};

page.open(body.url, function () {
Expand Down Expand Up @@ -76,8 +104,18 @@ page.open(body.url, function () {
}

setTimeout(function () {
resolvePage()
}, body.printDelay || 0);

function resolvePage() {
if (body.waitForJS && !pageJSisDone) {
setTimeout(function() {
resolvePage();
}, 100);
return;
}

page.render(body.output, body.format);
respond(page, body);

}, body.printDelay || 0);
}
});
11 changes: 10 additions & 1 deletion lib/scripts/standaloneScriptPart.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,16 @@ stream.close();

function respond(page, body) {
console.log(body.numberOfPages);
phantom.exit(0);

// Work-around to avoid "Unsafe JavaScript attempt to access frame" warning in PhantomJS 1.9.8.
// See: https://github.com/ariya/phantomjs/issues/12697
// since we rely on stdout for the dedicated-process strategy this work-around
// ensures the phantom process don't log anything we don't want
page.close();

setTimeout(function() {
phantom.exit(0);
}, 0);
}

$conversion
Expand Down
9 changes: 8 additions & 1 deletion lib/serverStrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function ensurePhantom(cb) {
return;

starting = true;
// TODO: handle callback err for .start
phantom.start(function() {
started = true;
starting = false;
Expand All @@ -32,8 +33,14 @@ module.exports = function(options, requestOptions, id, cb) {
return cb(err);

phantom.execute(requestOptions, function (err, res) {
if (err)
if (err) {
// if the error is a timeout from phantom-workers
if (err.message === "Timeout") {
err.phantomTimeout = true;
}

return cb(err);
}

cb(null, {
stream: fs.createReadStream(requestOptions.output),
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"url": "[email protected]:pofider/phantom-html-to-pdf.git"
},
"dependencies": {
"phantom-workers": "0.3.0",
"phantom-workers": "0.3.1",
"phantomjs": "1.9.17",
"underscore": "1.8.2",
"uuid": "2.0.1"
Expand All @@ -29,7 +29,7 @@
"should": "5.0.1"
},
"scripts": {
"test": "node lib/prepareScripts.js && mocha test/test.js",
"test": "node lib/prepareScripts.js && mocha test/test.js --timeout 4000",
"postinstall": "node lib/prepareScripts.js"
},
"main": "index.js",
Expand Down
71 changes: 70 additions & 1 deletion test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe("phantom html to pdf", function () {
});
});

it ('should create a pdf file ignoring ssl errors', function(done) {
it('should create a pdf file ignoring ssl errors', function(done) {
conversion({
url: 'https://sygris.com'
}, function(err, res) {
Expand All @@ -61,6 +61,75 @@ describe("phantom html to pdf", function () {
done();
});
});

it('should wait for page js execution', function(done) {
conversion({
html: '<h1>aa</h1><script>window.PHANTOM_HTML_TO_PDF_READY = true;</script>',
waitForJS: true
}, function(err, res) {
if (err) {
return done(err);
}

res.numberOfPages.should.be.eql(1);
res.stream.should.have.property("readable");
done();
});
});

it('should wait for page async js execution', function(done) {
conversion({
html: '<h1>aa</h1><script>setTimeout(function() { window.PHANTOM_HTML_TO_PDF_READY = true; }, 200);</script>',
waitForJS: true
}, function(err, res) {
if (err) {
return done(err);
}

res.numberOfPages.should.be.eql(1);
res.stream.should.have.property("readable");
done();
});
});

it('should allow define a custom var name for page js execution', function(done) {
conversion({
html: '<h1>aa</h1><script>window.ready = true;</script>',
waitForJS: true,
waitForJSVarName: 'ready'
}, function(err, res) {
if (err) {
return done(err);
}

res.numberOfPages.should.be.eql(1);
res.stream.should.have.property("readable");
done();
});
});

it('should throw timeout when waiting for page js execution', function(done) {
//since phantom-worker doesn't support a timeout per request
//we increase the test timeout
this.timeout(20000);

conversion({
html: '<h1>aa</h1>',
timeout: 500,
waitForJS: true
}, function(err, res) {
if (!err) {
return done(new Error('the conversion doesn\'t throw error'));
}

if (err.phantomTimeout !== undefined) {
should(err.phantomTimeout).be.eql(true);
done();
} else {
done(err);
}
});
});
}

rmDir = function (dirPath) {
Expand Down

0 comments on commit adf62c7

Please sign in to comment.