1 | var fs = require('fs')
|
2 | var childprocess = require('child_process')
|
3 | var path = require('path')
|
4 | var assert = require('assert')
|
5 |
|
6 | try {
|
7 | var phantomjs = require('phantomjs-prebuilt')
|
8 | } catch (err) {
|
9 | console.log('html-pdf: Failed to load PhantomJS module.', err)
|
10 | }
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 | module.exports = PDF
|
26 | function PDF (html, options) {
|
27 | this.html = html
|
28 | this.options = options || {}
|
29 | if (this.options.script) {
|
30 | this.script = path.normalize(this.options.script)
|
31 | } else {
|
32 | this.script = path.join(__dirname, 'scripts', 'pdf_a4_portrait.js')
|
33 | }
|
34 |
|
35 | if (this.options.filename) this.options.filename = path.resolve(this.options.filename)
|
36 | if (!this.options.phantomPath) this.options.phantomPath = phantomjs && phantomjs.path
|
37 | this.options.phantomArgs = this.options.phantomArgs || []
|
38 | assert(this.options.phantomPath, "html-pdf: Failed to load PhantomJS module. You have to set the path to the PhantomJS binary using 'options.phantomPath'")
|
39 | assert(typeof this.html === 'string' && this.html.length, "html-pdf: Can't create a pdf without an html string")
|
40 | this.options.timeout = parseInt(this.options.timeout, 10) || 30000
|
41 | }
|
42 |
|
43 | PDF.prototype.toBuffer = function PdfToBuffer (callback) {
|
44 | this.exec(function execPdfToBuffer (err, res) {
|
45 | if (err) return callback(err)
|
46 | fs.readFile(res.filename, function readCallback (err, buffer) {
|
47 | if (err) return callback(err)
|
48 | fs.unlink(res.filename, function unlinkPdfFile (err) {
|
49 | if (err) return callback(err)
|
50 | callback(null, buffer)
|
51 | })
|
52 | })
|
53 | })
|
54 | }
|
55 |
|
56 | PDF.prototype.toStream = function PdfToStream (callback) {
|
57 | this.exec(function (err, res) {
|
58 | if (err) return callback(err)
|
59 | try {
|
60 | var stream = fs.createReadStream(res.filename)
|
61 | } catch (err) {
|
62 | return callback(err)
|
63 | }
|
64 |
|
65 | stream.on('end', function () {
|
66 | fs.unlink(res.filename, function unlinkPdfFile (err) {
|
67 | if (err) console.log('html-pdf:', err)
|
68 | })
|
69 | })
|
70 |
|
71 | callback(null, stream)
|
72 | })
|
73 | }
|
74 |
|
75 | PDF.prototype.toFile = function PdfToFile (filename, callback) {
|
76 | assert(arguments.length > 0, 'html-pdf: The method .toFile([filename, ]callback) requires a callback.')
|
77 | if (filename instanceof Function) {
|
78 | callback = filename
|
79 | filename = undefined
|
80 | } else {
|
81 | this.options.filename = path.resolve(filename)
|
82 | }
|
83 | this.exec(callback)
|
84 | }
|
85 |
|
86 | PDF.prototype.exec = function PdfExec (callback) {
|
87 | var child = childprocess.spawn(this.options.phantomPath, [].concat(this.options.phantomArgs, [this.script]), this.options.childProcessOptions)
|
88 | var stderr = []
|
89 |
|
90 | var timeout = setTimeout(function execTimeout () {
|
91 | respond(null, new Error('html-pdf: PDF generation timeout. Phantom.js script did not exit.'))
|
92 | }, this.options.timeout)
|
93 |
|
94 | function onError (buffer) {
|
95 | stderr.push(buffer)
|
96 | }
|
97 |
|
98 | function onData (buffer) {
|
99 | var result
|
100 | try {
|
101 | var json = buffer.toString().trim()
|
102 | if (json) result = JSON.parse(json)
|
103 | } catch (err) {
|
104 |
|
105 | process.stdout.write(buffer)
|
106 | }
|
107 |
|
108 | if (result) respond(null, null, result)
|
109 | }
|
110 |
|
111 | var callbacked = false
|
112 | function respond (code, err, data) {
|
113 | if (callbacked) return
|
114 | callbacked = true
|
115 | clearTimeout(timeout)
|
116 |
|
117 |
|
118 | if (code === null) kill(child, onData, onError)
|
119 |
|
120 | if (!data) {
|
121 | if (!err && code) err = new Error("html-pdf: Received the exit code '" + code + "'")
|
122 | else if (!err) err = new Error('html-pdf: Unknown Error')
|
123 |
|
124 | var postfix = stderr.length ? '\n' + Buffer.concat(stderr).toString() : ''
|
125 | if (postfix) err.message += postfix
|
126 | return callback(err, null)
|
127 | }
|
128 |
|
129 | callback(null, data)
|
130 | }
|
131 |
|
132 | child.stdout.on('data', onData)
|
133 | child.stderr.on('data', onError)
|
134 | child.on('error', function onError (err) { respond(null, err) })
|
135 |
|
136 |
|
137 | child.on('close', respond)
|
138 | child.on('exit', respond)
|
139 |
|
140 | var config = JSON.stringify({html: this.html, options: this.options})
|
141 | child.stdin.write(config + '\n', 'utf8')
|
142 | child.stdin.end()
|
143 | }
|
144 |
|
145 | function kill (child, onData, onError) {
|
146 | child.stdin.end()
|
147 | child.kill()
|
148 | }
|