Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 88 additions & 54 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,54 @@ class PdfTk {
.replace(/\)/g, '\\)');
}

/**
* Converts a string to a buffer and appends it to an array of buffers.
* @static
* @public
* @param {Array} buffers - an array of buffers to which the result will be concatenated
* @param {String} str - String to buffer.
* @param {String} encoding - encodding
* @returns {Number} - size of the buffer
*/
static stringToBuffers(buffers, str, encoding = 'utf8') {
const buffer = PdfTk.stringToBuffer(str, encoding);
buffers.push(buffer);
return buffer.length;
}

/**
* Returns a buffer from a JSON object representing a form field.
* @static
* @public
* @param {String} name - string representing a (partial) field name in the fdf.
* @param {Object} value - JSON object representing the value to be added to the fdf.
* @returns {Buffer} Fdf data as a buffer.
*/
static fdfFieldToBuffer(name, value) {
const buffers = [];
let len = 0;
if (value !== null && value !== undefined) {
len += PdfTk.stringToBuffers(buffers, '<<\n/T (');
len += PdfTk.stringToBuffers(buffers, PdfTk.sanitizeForFdf(name.toString()), 'binary');
len += PdfTk.stringToBuffers(buffers, ')\n');
if (typeof value === 'object') {
len += PdfTk.stringToBuffers(buffers, '/Kids [\n');
for (const prop in value) {
const buf = PdfTk.fdfFieldToBuffer(prop, value[prop]);
len += buf.length;
buffers.push(buf);
}
len += PdfTk.stringToBuffers(buffers, ']\n');
} else {
len += PdfTk.stringToBuffers(buffers, '/V (');
len += PdfTk.stringToBuffers(buffers, PdfTk.sanitizeForFdf(value.toString()), 'binary');
len += PdfTk.stringToBuffers(buffers, ')\n');
}
len += PdfTk.stringToBuffers(buffers, '>>\n');
}
return Buffer.concat(buffers, len);
}

/**
* Creates fdf file from JSON input.
* Converts input values to binary buffer, which seems to allow PdfTk to render utf-8 characters.
Expand All @@ -199,63 +247,39 @@ class PdfTk {
* @returns {Buffer} Fdf data as a buffer.
*/
static generateFdfFromJSON(data) {

const header = PdfTk.stringToBuffer(`
%FDF-1.2\n
${String.fromCharCode(226) + String.fromCharCode(227) + String.fromCharCode(207) + String.fromCharCode(211)}\n
1 0 obj\n
<<\n
/FDF\n
<<\n
/Fields [\n
`);

let body = PdfTk.stringToBuffer('');
const buffers = [];
let len = 0;
len += PdfTk.stringToBuffers(buffers, `%FDF-1.2
%${String.fromCharCode(226) + String.fromCharCode(227) + String.fromCharCode(207) + String.fromCharCode(211)}
1 0 obj
<<
/FDF
<<
/Fields [
`, 'binary');

for (const prop in data) {
/* istanbul ignore else */
if (data.hasOwnProperty(prop) && data[prop] !== null && data[prop] !== undefined) {
body = Buffer.concat([
body,
PdfTk.stringToBuffer('<<\n/T ('),
]);
body = Buffer.concat([
body,
PdfTk.stringToBuffer(PdfTk.sanitizeForFdf(prop.toString()), 'binary'),
]);
body = Buffer.concat([
body,
PdfTk.stringToBuffer(')\n/V('),
]);
body = Buffer.concat([
body,
PdfTk.stringToBuffer(PdfTk.sanitizeForFdf(data[prop].toString()), 'binary'),
]);
body = Buffer.concat([
body,
PdfTk.stringToBuffer(')\n>>\n'),
]);
if (data.hasOwnProperty(prop)) {
const buf = PdfTk.fdfFieldToBuffer(prop, data[prop]);
len += buf.length;
buffers.push(buf);
}
}

const footer = PdfTk.stringToBuffer(`
]\n
>>\n
>>\n
endobj \n
trailer\n
\n
<<\n
/Root 1 0 R\n
>>\n
%%EOF\n
`);

return Buffer.concat([
header,
body,
footer,
]);
len += PdfTk.stringToBuffers(buffers, `]
>>
>>
endobj
trailer

<<
/Root 1 0 R
>>
%%EOF
`, 'binary');

return Buffer.concat(buffers, len);

}

Expand Down Expand Up @@ -341,9 +365,19 @@ class PdfTk {
const result = [];

child.stderr.on('data', data => {
if (!this._ignoreWarnings && data.toString().toLowerCase().includes('error')) {
this._cleanUpTempFiles();
return reject(data.toString('utf8'));
if (!this._ignoreWarnings) {
let str = data.toString();
if (str.toLowerCase().includes('error')) {
this._cleanUpTempFiles();
try {
str = data.toString('utf8');
} catch (e) {
// toString('utf8') can throw when toString() (above) did not...
// if it does, ignore this secondary error and
// reject using the string we already have.
}
return reject(str);
}
}
});

Expand Down
3 changes: 3 additions & 0 deletions pretest.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,21 @@ function sequentialPromise(operations) {

const commands = [
'test/files/form.pdf fill_form test/files/form.fdf output test/files/filledform.temp.pdf',
'test/files/form2.pdf fill_form test/files/form2.fdf output test/files/filledform2.temp.pdf',
'test/files/form.pdf fill_form test/files/formwithnumber.fdf output test/files/filledformwithnumber.temp.pdf',
'test/files/form.pdf fill_form test/files/form.fdf output test/files/filledformflat.temp.pdf flatten',
'test/files/form.pdf fill_form test/files/form.blank.fdf output test/files/filledformempty.temp.pdf flatten',
'test/files/form.pdf fill_form test/files/form.escape.fdf output test/files/filledformescape.temp.pdf flatten',
'test/files/form.pdf generate_fdf output test/files/form.temp.fdf',
'test/files/form2.pdf generate_fdf output test/files/form2.temp.fdf',
'test/files/form.pdf stamp test/files/logo.pdf output test/files/stamp.temp.pdf',
'test/files/form.pdf multistamp test/files/logo.pdf output test/files/multistamp.temp.pdf',
'A=test/files/document1.pdf B=test/files/document2.pdf cat A B output test/files/documentcatwithdate.temp.pdf keep_final_id', 'test/files/documentcatwithdate.temp.pdf update_info_utf8 test/files/documentcat.info output test/files/documentcat.temp.pdf',
'test/files/form.pdf background test/files/logo.pdf output test/files/background.temp.pdf',
'test/files/form.pdf multibackground test/files/logo.pdf output test/files/multibackground.temp.pdf',
'test/files/form.pdf dump_data_fields output test/files/form.fields.temp.info',
'test/files/form.pdf dump_data_fields_utf8 output test/files/form.fields.utf8.temp.info',
'test/files/form2.pdf dump_data_fields_utf8 output test/files/form2.fields.utf8.temp.info',
].map(i => pdftk.bind(this, i));


Expand Down
13 changes: 13 additions & 0 deletions test/dumpDataFieldsUtf8.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ describe('dumpDataFieldsUtf8', function () {
.catch(err => expect(err).to.be.null);
});

it('should output hierarchical data fields to file with file path input', function () {

const testFile = fs.readFileSync(path.join(__dirname, './files/form2.fields.utf8.temp.info'));
const input = path.join(__dirname, './files/form2.pdf');

return pdftk
.input(input)
.dumpDataFieldsUtf8()
.output()
.then(buffer => expect(buffer.equals(testFile)).to.be.true)
.catch(err => expect(err).to.be.null);
});

it('should output data fields to file with buffer input', function () {

const testFile = fs.readFileSync(path.join(__dirname, './files/form.fields.utf8.temp.info'));
Expand Down
35 changes: 35 additions & 0 deletions test/files/form2.fdf
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
%FDF-1.2
%âãÏÓ
1 0 obj
<<
/FDF
<<
/Fields [
<<
/T (topmostSubform[0])
/Kids [
<<
/T (Page1[0])
/Kids [
<<
/T (f1_1[0])
/V (John Doe)
>>
<<
/T (f1_2[0])
/V (123-12-3123)
>>
]
>>
]
>>
]
>>
>>
endobj
trailer

<<
/Root 1 0 R
>>
%%EOF
Binary file added test/files/form2.pdf
Binary file not shown.
23 changes: 23 additions & 0 deletions test/fillForm.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,29 @@ describe('fillForm', function () {
.catch(err => expect(err).to.be.null);
});

it('should fill a form featuring partially qualified field names (hierarchical/grouped fields)', function () {

const testFile = fs.readFileSync(path.join(__dirname, './files/filledform2.temp.pdf'));
const input = path.join(__dirname, './files/form2.pdf');

return pdftk
.input(input)
.fillForm({
'topmostSubform[0]': {
'Page1[0]': {
'f1_1[0]': 'John Doe',
'f1_2[0]': '123-12-3123',
},
},
})
.output()
.then(buffer => expect(buffer.equals(testFile)).to.be.true)
.catch(err => {
if (err) console.error(err);
return expect(err).to.be.null;
});
});

it('should fill a form without flattening it with a number input', function () {

const testFile = fs.readFileSync(path.join(__dirname, './files/filledformwithnumber.temp.pdf'));
Expand Down
13 changes: 13 additions & 0 deletions test/generateFdf.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ describe('generateFdf', function () {
.catch(err => expect(err).to.be.null);
});

it('should generate a hierarchical fdf from a pdf containing partially qualified field names', function () {

const testFile = fs.readFileSync(path.join(__dirname, './files/form2.temp.fdf'));
const input = path.join(__dirname, './files/form2.pdf');

return pdftk
.input(input)
.generateFdf()
.output()
.then(buffer => expect(buffer.equals(testFile)).to.be.true)
.catch(err => expect(err).to.be.null);
});

it('should generate an fdf file from a pdf with a buffer input', function () {

const testFile = fs.readFileSync(path.join(__dirname, './files/form.temp.fdf'));
Expand Down