├── .gitignore ├── .travis.yml ├── README.md ├── example-unclipped.js ├── example-unclipped.png ├── example.js ├── example.png ├── index.js ├── package-lock.json ├── package.json ├── script └── render.js └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # capture-phantomjs 2 | 3 | Capture screenshots using [phantomjs](http://phantomjs.org/). 4 | 5 | [![build status](https://secure.travis-ci.org/juliangruber/capture-phantomjs.png)](http://travis-ci.org/juliangruber/capture-phantomjs) [![Greenkeeper badge](https://badges.greenkeeper.io/juliangruber/capture-phantomjs.svg)](https://greenkeeper.io/) 6 | 7 | ## Example 8 | 9 | Capture a `1024x768` screenshot of [twitter.com](http://twitter.com): 10 | 11 | ```js 12 | const capture = require('capture-phantomjs') 13 | const fs = require('fs') 14 | 15 | capture({ 16 | url: 'https://twitter.com/', 17 | width: 1024, 18 | height: 768 19 | }).then(screenshot => { 20 | fs.writeFileSync(`${__dirname}/example.png`, screenshot) 21 | console.log('open example.png') 22 | }) 23 | ``` 24 | 25 | ![github.com](https://raw.github.com/juliangruber/capture-phantomjs/master/example.png) 26 | 27 | ## API 28 | 29 | ### screenshot({ url, width = 1024, height = 768, wait = 0, format = 'png', clip = true, cookies = [], ignoreSSLErrors = false, SSLCertificatesPath, SSLProtocol }) 30 | 31 | Capture a screenshot of `url`, returns a `Promise` which resolves with a buffer. 32 | 33 | Options: 34 | 35 | - `url` Page url 36 | - `width` Viewport width 37 | - `height` Viewport height 38 | - `wait` Time in `ms` to wait after the page finished loading all initial resources 39 | - `format` File format (`png`, `jpg`, `gif`) 40 | - `clip` Cut the image to exact dimensions, removing the fold 41 | - `cookies` Array of cookie objects, see below 42 | - `ignoreSSLErrors` ignore SSL errors 43 | - `SSLCertificatesPath` path for PhantomJS to look for SSL certificates 44 | - `SSLProtocol` Supported protocols: `sslv3`, `sslv2`, `tlsv1`, `any` 45 | 46 | ### Cookie format 47 | 48 | The cookie format is this: 49 | 50 | ```js 51 | { 52 | name : 'valid-cookie-name', // required 53 | value : 'valid-cookie-value', // required 54 | domain : 'the-domain.com', // required 55 | path : '/', 56 | httponly : true, 57 | secure : false, 58 | expires : (new Date()).getTime() + 3600 // expires in 1 hour 59 | } 60 | ``` 61 | 62 | ## Installation 63 | 64 | With [npm](https://npmjs.org) do: 65 | 66 | ```bash 67 | npm install capture-phantomjs 68 | ``` 69 | 70 | This project requires `nodejs@8.0.0` or later. 71 | 72 | ## CI 73 | 74 | This project will work in CI environments like Travis and AppVeyor without any additional configuration. It fetches phantomjs itself and doesn't need a X server to run. 75 | 76 | ## Related projects 77 | 78 | - __[capture-screenshot](https://github.com/juliangruber/capture-screenshot)__ — Capture screenshots in multiple browsers 79 | - __[capture-chrome](https://github.com/juliangruber/capture-chrome)__ — Capture screenshots using Chrome 80 | - __[capture-electron](https://github.com/juliangruber/capture-electron)__ — Capture screenshots using Electron 81 | 82 | ## License 83 | 84 | (MIT) 85 | 86 | Copyright (c) 2017 Julian Gruber <julian@juliangruber.com> 87 | 88 | Permission is hereby granted, free of charge, to any person obtaining a copy of 89 | this software and associated documentation files (the "Software"), to deal in 90 | the Software without restriction, including without limitation the rights to 91 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 92 | of the Software, and to permit persons to whom the Software is furnished to do 93 | so, subject to the following conditions: 94 | 95 | The above copyright notice and this permission notice shall be included in all 96 | copies or substantial portions of the Software. 97 | 98 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 99 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 100 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 101 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 102 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 103 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 104 | SOFTWARE. 105 | -------------------------------------------------------------------------------- /example-unclipped.js: -------------------------------------------------------------------------------- 1 | const capture = require('.') 2 | const fs = require('fs') 3 | 4 | capture({ 5 | url: 'https://twitter.com/', 6 | width: 1024, 7 | height: 768, 8 | clip: false 9 | }).then(screenshot => { 10 | fs.writeFileSync(`${__dirname}/example-unclipped.png`, screenshot) 11 | console.log('open example-unclipped.png') 12 | }) 13 | -------------------------------------------------------------------------------- /example-unclipped.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliangruber/capture-phantomjs/747ac5efca15fd84db3009c96d3563d0c6849fe8/example-unclipped.png -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const capture = require('.') 2 | const fs = require('fs') 3 | 4 | capture({ 5 | url: 'https://twitter.com/', 6 | width: 1024, 7 | height: 768 8 | }).then(screenshot => { 9 | fs.writeFileSync(`${__dirname}/example.png`, screenshot) 10 | console.log('open example.png') 11 | }) 12 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/juliangruber/capture-phantomjs/747ac5efca15fd84db3009c96d3563d0c6849fe8/example.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const { promisify } = require('util') 2 | const exec = promisify(require('child_process').exec) 3 | const { path: phantomjs } = require('phantomjs') 4 | const { Buffer } = require('safe-buffer') 5 | const escape = require('shell-escape') 6 | 7 | const If = (cond, val) => (cond ? val : '') 8 | 9 | module.exports = ({ 10 | url, 11 | width: width = 1024, 12 | height: height = 768, 13 | wait: wait = 0, 14 | format: format = 'png', 15 | clip: clip = true, 16 | cookies: cookies = [], 17 | ignoreSSLErrors: ignoreSSLErrors = false, 18 | SSLCertificatesPath, 19 | SSLProtocol 20 | }) => 21 | exec( 22 | escape([ 23 | phantomjs, 24 | `--ignore-ssl-errors=${ignoreSSLErrors}`, 25 | If(SSLCertificatesPath, `--ssl-certificates-path=${SSLCertificatesPath}`), 26 | If(SSLProtocol, `--ssl-protocol=${SSLProtocol}`), 27 | `${__dirname}/script/render.js`, 28 | url, 29 | width, 30 | height, 31 | wait, 32 | format.toUpperCase(), 33 | clip, 34 | JSON.stringify(cookies) 35 | ]), 36 | { maxBuffer: Infinity } 37 | ).then(({ stdout }) => Buffer.from(stdout, 'base64')) 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "capture-phantomjs", 3 | "description": "Capture screenshots using phantomjs", 4 | "version": "1.4.5", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/juliangruber/capture-phantomjs.git" 8 | }, 9 | "scripts": { 10 | "test": "prettier-standard '**/*.js' && standard && tap --reporter=tap test.js" 11 | }, 12 | "homepage": "https://github.com/juliangruber/capture-phantomjs", 13 | "main": "index.js", 14 | "dependencies": { 15 | "phantomjs": "^2.1.7", 16 | "safe-buffer": "^5.1.2", 17 | "shell-escape": "^0.2.0" 18 | }, 19 | "devDependencies": { 20 | "prettier-standard": "^16.0.0", 21 | "standard": "^14.0.0", 22 | "tap": "^12.0.1" 23 | }, 24 | "keywords": [ 25 | "phantomjs", 26 | "screenshot", 27 | "headless" 28 | ], 29 | "author": { 30 | "name": "Julian Gruber", 31 | "email": "mail@juliangruber.com", 32 | "url": "http://juliangruber.com" 33 | }, 34 | "license": "MIT" 35 | } 36 | -------------------------------------------------------------------------------- /script/render.js: -------------------------------------------------------------------------------- 1 | /* global phantom */ 2 | 3 | var webpage = require('webpage') 4 | var args = require('system').args 5 | var noop = function () {} 6 | 7 | var url = args[1] 8 | var width = args[2] 9 | var height = args[3] 10 | var wait = Number(args[4]) 11 | var format = args[5] 12 | var clip = args[6] === 'true' 13 | var cookies = JSON.parse(args[7]) 14 | 15 | var page = webpage.create() 16 | page.viewportSize = { 17 | width: width, 18 | height: height 19 | } 20 | page.clipRect = { 21 | top: 0, 22 | left: 0, 23 | width: clip ? width : 0, 24 | height: clip ? height : 0 25 | } 26 | 27 | cookies.forEach(function (cookie) { 28 | phantom.addCookie(cookie) 29 | }) 30 | 31 | page.onConsoleMessage = page.onConfirm = page.onPrompt = page.onError = noop 32 | 33 | page.open(url, function (status) { 34 | if (status !== 'success') { 35 | console.error('Unable to load') 36 | phantom.exit() 37 | } 38 | window.setTimeout(function () { 39 | page.evaluate(function () { 40 | if (!document.body.style.background) { 41 | document.body.style.backgroundColor = 'white' 42 | } 43 | }) 44 | console.log(page.renderBase64(format)) 45 | phantom.exit() 46 | }, wait) 47 | }) 48 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const { test } = require('tap') 2 | const screenshot = require('.') 3 | const http = require('http') 4 | 5 | let server, url 6 | 7 | test('setup', t => { 8 | server = http.createServer((req, res) => res.end('ohai!')) 9 | server.listen(() => { 10 | url = `http://localhost:${server.address().port}` 11 | t.end() 12 | }) 13 | }) 14 | 15 | test('screenshot', async t => { 16 | const pic = await screenshot({ url }) 17 | t.ok(pic) 18 | t.ok(Buffer.isBuffer(pic)) 19 | }) 20 | 21 | test('cleanup', t => { 22 | server.close() 23 | t.end() 24 | }) 25 | --------------------------------------------------------------------------------