You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
148 lines
4.5 KiB
148 lines
4.5 KiB
var fs = require('fs')
|
|
var childprocess = require('child_process')
|
|
var path = require('path')
|
|
var assert = require('assert')
|
|
|
|
try {
|
|
var phantomjs = require('phantomjs-prebuilt')
|
|
} catch (err) {
|
|
console.log('html-pdf: Failed to load PhantomJS module.', err)
|
|
}
|
|
|
|
/*
|
|
* phantomjs version 1.8.1 and later should work.
|
|
*
|
|
* Create a PDF file out of an html string.
|
|
*
|
|
* Regions for the PDF page are:
|
|
*
|
|
* - Page Header -> document.getElementById('pageHeader')
|
|
* - Page Content -> document.getElementById('pageContent')
|
|
* - Page Footer -> document.getElementById('pageFooter')
|
|
*
|
|
* When no #pageContent is available, phantomjs will use document.body as pdf content
|
|
*/
|
|
module.exports = PDF
|
|
function PDF (html, options) {
|
|
this.html = html
|
|
this.options = options || {}
|
|
if (this.options.script) {
|
|
this.script = path.normalize(this.options.script)
|
|
} else {
|
|
this.script = path.join(__dirname, 'scripts', 'pdf_a4_portrait.js')
|
|
}
|
|
|
|
if (this.options.filename) this.options.filename = path.resolve(this.options.filename)
|
|
if (!this.options.phantomPath) this.options.phantomPath = phantomjs && phantomjs.path
|
|
this.options.phantomArgs = this.options.phantomArgs || []
|
|
assert(this.options.phantomPath, "html-pdf: Failed to load PhantomJS module. You have to set the path to the PhantomJS binary using 'options.phantomPath'")
|
|
assert(typeof this.html === 'string' && this.html.length, "html-pdf: Can't create a pdf without an html string")
|
|
this.options.timeout = parseInt(this.options.timeout, 10) || 30000
|
|
}
|
|
|
|
PDF.prototype.toBuffer = function PdfToBuffer (callback) {
|
|
this.exec(function execPdfToBuffer (err, res) {
|
|
if (err) return callback(err)
|
|
fs.readFile(res.filename, function readCallback (err, buffer) {
|
|
if (err) return callback(err)
|
|
fs.unlink(res.filename, function unlinkPdfFile (err) {
|
|
if (err) return callback(err)
|
|
callback(null, buffer)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
PDF.prototype.toStream = function PdfToStream (callback) {
|
|
this.exec(function (err, res) {
|
|
if (err) return callback(err)
|
|
try {
|
|
var stream = fs.createReadStream(res.filename)
|
|
} catch (err) {
|
|
return callback(err)
|
|
}
|
|
|
|
stream.on('end', function () {
|
|
fs.unlink(res.filename, function unlinkPdfFile (err) {
|
|
if (err) console.log('html-pdf:', err)
|
|
})
|
|
})
|
|
|
|
callback(null, stream)
|
|
})
|
|
}
|
|
|
|
PDF.prototype.toFile = function PdfToFile (filename, callback) {
|
|
assert(arguments.length > 0, 'html-pdf: The method .toFile([filename, ]callback) requires a callback.')
|
|
if (filename instanceof Function) {
|
|
callback = filename
|
|
filename = undefined
|
|
} else {
|
|
this.options.filename = path.resolve(filename)
|
|
}
|
|
this.exec(callback)
|
|
}
|
|
|
|
PDF.prototype.exec = function PdfExec (callback) {
|
|
var child = childprocess.spawn(this.options.phantomPath, [].concat(this.options.phantomArgs, [this.script]), this.options.childProcessOptions)
|
|
var stderr = []
|
|
|
|
var timeout = setTimeout(function execTimeout () {
|
|
respond(null, new Error('html-pdf: PDF generation timeout. Phantom.js script did not exit.'))
|
|
}, this.options.timeout)
|
|
|
|
function onError (buffer) {
|
|
stderr.push(buffer)
|
|
}
|
|
|
|
function onData (buffer) {
|
|
var result
|
|
try {
|
|
var json = buffer.toString().trim()
|
|
if (json) result = JSON.parse(json)
|
|
} catch (err) {
|
|
// Proxy for debugging purposes
|
|
process.stdout.write(buffer)
|
|
}
|
|
|
|
if (result) respond(null, null, result)
|
|
}
|
|
|
|
var callbacked = false
|
|
function respond (code, err, data) {
|
|
if (callbacked) return
|
|
callbacked = true
|
|
clearTimeout(timeout)
|
|
|
|
// If we don't have an exit code, we kill the process, ignore stderr after this point
|
|
if (code === null) kill(child, onData, onError)
|
|
|
|
if (!data) {
|
|
if (!err && code) err = new Error("html-pdf: Received the exit code '" + code + "'")
|
|
else if (!err) err = new Error('html-pdf: Unknown Error')
|
|
|
|
var postfix = stderr.length ? '\n' + Buffer.concat(stderr).toString() : ''
|
|
if (postfix) err.message += postfix
|
|
return callback(err, null)
|
|
}
|
|
|
|
callback(null, data)
|
|
}
|
|
|
|
child.stdout.on('data', onData)
|
|
child.stderr.on('data', onError)
|
|
child.on('error', function onError (err) { respond(null, err) })
|
|
|
|
// An exit event is most likely an error because we didn't get any data at this point
|
|
child.on('close', respond)
|
|
child.on('exit', respond)
|
|
|
|
var config = JSON.stringify({html: this.html, options: this.options})
|
|
child.stdin.write(config + '\n', 'utf8')
|
|
child.stdin.end()
|
|
}
|
|
|
|
function kill (child, onData, onError) {
|
|
child.stdin.end()
|
|
child.kill()
|
|
}
|
|
|