node.jsからPhantomJSを使ってPDFを生成する


昨日はPhantomJSをインストール、コマンドラインからPDFや画像を生成してみました
今日は実際の用途に使われそうな、node.js(+ ExpressJS)からPhantomJSを使う方法を検証してみました。
例によってプロジェクトの作成から行います。なお、PhantomJS自体はインストールされている前提で行います。

プロジェクトの作成

node-phantomを使いました。

$ express -e phantom_sample/
$ cd phantom_sample/
$ npm install
$ npm install node-phantom --save
$ mkdir output

outputディレクトリは、生成したファイルを一時的に格納するディレクトリです。詳細は後述します。

app.js

至って普通です。ルーティングで'/pdf'を追加しただけです。

var express = require('express')
  , routes = require('./routes')
  , http = require('http')
  , path = require('path');

var app = express();

app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'public')));
});

app.configure('development', function(){
  app.use(express.errorHandler());
});

app.get('/', routes.index);
app.get('/pdf', routes.pdf);

http.createServer(app).listen(app.get('port'), function(){
  console.log("Express server listening on port " + app.get('port'));
});

views/index.ejs

[追記]

<meta charset="UTF-8">

の記述が無いと日本語が化けました。

routes/index.js

exports.pdfを追加します。
内容としては、

  • 元になるHTMLファイルをoutputディレクトリに生成(res.render())
  • 生成したHTMLファイルからPDFファイルを作成(page.render())
  • 生成したPDFをブラウザに出力(res.send())

となります。

exports.pdf = function(req, res){
	res.render('index', { title: 'Express' }, function(err, html){
		//HTML書き込み
		var fs = require('fs');
		var filename = 'output/index.html';
		fs.writeFile(filename, html, function (err) {
			if (err) throw err;

			//PDF作成
			var phantom = require('node-phantom');
			phantom.create(function(err, ph) {
				if (err) throw err;
				ph.createPage(function(err, page) {
					if (err) throw err;

					//page.set('viewportSize', { width: 600, height: 600 });
					page.set('paperSize', {
						format: "A4",
						orientation: "portrait",
						margin: {left:"2.5cm", right:"2.5cm", top:"1cm", bottom:"1cm"}
					});

					page.open(filename, function(err, status) {
						var pdffile = 'output/index.pdf';
						page.render(pdffile);
						page.close(function(){
							var fs = require('fs');
							fs.readFile(pdffile, function (err, data) {
								if (err) throw err;

								res.set({
									'Content-Type': 'application/pdf',
									'Content-Disposition': 'attachment; filename="downloaded.pdf"'
								});
								res.send(data);
							});
						});
						ph.exit(function(){
							console.log('exit');
						});
					});
				});
			});
		});
	});
};

ドキュメントが揃ってないので、正直手探りでした。
page.set()で用紙サイズやらを設定するのは、test/testpagesetget.jsにありました。他の項目はそちらを参照ください。
ph.exit()にコールバックが設定してあるのは、page.close()で問題なさそうですが、念のためこちらでres.send()した方が良いのかもという理由で残してあります。

URIでダウンロードさせるだけなら、publicディレクトリにPDFを生成して、生成後にリダイレクトさせれば良いと思います。