fill-pdf
Version:
Fill out pdf forms
147 lines (118 loc) • 4.07 kB
JavaScript
;
const fs = require('fs'),
path = require('path'),
spawn = require('child_process').spawn,
async = require('async'),
temp = require('temp');
exports.generateFdf = function(data) {
var header, body, footer;
header = new Buffer('%FDF-1.2\n\u00e2\u00e3\u00cf\u00d3\n1 0 obj \n<<\n/FDF \n<<\n/Fields [\n');
footer = new Buffer(']\n>>\n>>\nendobj \ntrailer\n\n<<\n/Root 1 0 R\n>>\n%%EOF\n');
body = new Buffer([]);
for(let key in data) {
var name = escapeFdf(key);
var value = escapeFdf(data[key]);
body = Buffer.concat([ body, new Buffer('<<\n/T (')]);
body = Buffer.concat([ body, name]);
body = Buffer.concat([ body, new Buffer(')\n/V (')]);
body = Buffer.concat([ body, value]);
body = Buffer.concat([ body, new Buffer(')\n>>\n')]);
}
var fdf = Buffer.concat([ header, body, footer ]);
return fdf;
};
exports.generatePdf = function(data, templatePath, extendArgs, callback) {
let tempNameResult = temp.path({suffix: '.pdf'}),
pdfPath = isAbsolute(templatePath) ? templatePath : path.join(__dirname, templatePath);
let normalized = normalizeArgs(extendArgs, callback);
extendArgs = normalized.args;
callback = normalized.callback;
let processArgs = [pdfPath, 'fill_form', '-', 'output', tempNameResult].concat(extendArgs);
let child = spawn('pdftk', processArgs);
handlePdftkError(child, callback);
handlePdftkExit(child, tempNameResult, callback);
writeFdfToPdftk(child, data);
};
// Escape data and return it as a buffer
function escapeFdf(data) {
let escaped = new Buffer([]);
let buf;
if(typeof data === 'string' || data instanceof Buffer) {
buf = new Buffer(data);
} else if(typeof data.toString === 'function') {
buf = new Buffer(data.toString());
} else {
buf = new Buffer(Object.prototype.toString.call(data));
}
for(let i=0; i<buf.length; i++) {
let c1 = String.fromCharCode(buf[i]);
let c2 = String.fromCharCode(buf[i+1]);
if(c1 === '(' || c1 === ')') {
escaped = Buffer.concat([escaped, new Buffer('\\' + c1)]);
} else if(c1 === '\r' && c2 === '\n') {
escaped = Buffer.concat([escaped, new Buffer('\r')]);
} else {
escaped = Buffer.concat([escaped, new Buffer([buf[i]])]);
}
}
return escaped;
}
function normalizeArgs(extendArgs, callback) {
// Check if extendArgs is our callback, adds backwards compat
if (typeof extendArgs === 'function') {
callback = extendArgs;
extendArgs = [];
}
else if (!(extendArgs instanceof Array)) {
extendArgs = [];
}
return { args: extendArgs, callback: callback };
}
function writeFdfToPdftk(child, data) {
child.stdin.write(exports.generateFdf(data));
child.stdin.end();
}
function handlePdftkError(child, callback) {
child.on('error', function (err) {
callback(err);
});
child.stderr.on('data', function (data) {
console.error('stderr: ' + data);
});
}
function handlePdftkExit(child, tempNameResult, callback) {
child.on('exit', createHandlePdftkExit(tempNameResult, callback));
}
function createHandlePdftkExit(tempNameResult, callback) {
return function(code) {
if ( code ) {
return callback(new Error('Non 0 exit code from pdftk spawn: ' + code));
}
async.waterfall([
(cb) => {
fs.readFile(tempNameResult, (err, filledPdf) => cb(err, filledPdf));
},
(filledPdf, cb) => {
fs.unlink(tempNameResult, (err) => cb(err, filledPdf));
}
],
function(err, result) {
callback(err, result);
});
};
}
function normalizeArgs(extendArgs, callback) {
// Check if extendArgs is our callback, adds backwards compat
if (typeof extendArgs === 'function') {
callback = extendArgs;
extendArgs = [];
}
else if (!(extendArgs instanceof Array)) {
extendArgs = [];
}
return { args: extendArgs, callback: callback };
}
function isAbsolute(Path) {
return (path.isAbsolute && path.isAbsolute(Path)) ||
(path.normalize(Path + '/') === path.normalize(path.resolve(Path) + '/'));
}