Skip to content

Commit

Permalink
Merge 73be455 into 427eac9
Browse files Browse the repository at this point in the history
  • Loading branch information
dhensby authored Jan 7, 2021
2 parents 427eac9 + 73be455 commit 1f8ef4a
Show file tree
Hide file tree
Showing 14 changed files with 242 additions and 168 deletions.
3 changes: 3 additions & 0 deletions dist/helpers/pdfkit/abstract_reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class PDFAbstractReference {
throw new Error('Must be implemented by subclasses');
}

end() {// noop
}

}

var _default = PDFAbstractReference;
Expand Down
75 changes: 18 additions & 57 deletions dist/helpers/pdfkitAddPlaceholder.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
*/
const pdfkitAddPlaceholder = ({
pdf,
pdfBuffer,
reason,
contactInfo = 'emailfromp1289@gmail.com',
name = 'Name from p12',
Expand All @@ -50,74 +49,36 @@ const pdfkitAddPlaceholder = ({

}); // Check if pdf already contains acroform field

const acroFormPosition = pdfBuffer.lastIndexOf('/Type /AcroForm');
const isAcroFormExists = acroFormPosition !== -1;
let fieldIds = [];
let acroFormId;

if (isAcroFormExists) {
let acroFormStart = acroFormPosition; // 10 is the distance between "/Type /AcroForm" and AcroFrom ID

const charsUntilIdEnd = 10;
const acroFormIdEnd = acroFormPosition - charsUntilIdEnd; // Let's find AcroForm ID by trying to find the "\n" before the ID
// 12 is a enough space to find the "\n" (generally it's 2 or 3, but I'm giving a big space though)

const maxAcroFormIdLength = 12;
let foundAcroFormId = '';

for (let index = charsUntilIdEnd + 1; index < charsUntilIdEnd + maxAcroFormIdLength; index++) {
let acroFormIdString = pdfBuffer.slice(acroFormPosition - index, acroFormIdEnd).toString();

if (acroFormIdString[0] == '\n') {
break;
}

foundAcroFormId = acroFormIdString;
acroFormStart = acroFormPosition - index;
}

const pdfSlice = pdfBuffer.slice(acroFormStart);
const acroForm = pdfSlice.slice(0, pdfSlice.indexOf('endobj')).toString();
acroFormId = parseInt(foundAcroFormId);
const acroFormFields = acroForm.slice(acroForm.indexOf('/Fields [') + 9, acroForm.indexOf(']'));
fieldIds = acroFormFields.split(' ').filter((element, index) => index % 3 === 0).map(fieldId => new _pdfkitReferenceMock.default(fieldId));
if (!pdf._acroform) {
pdf.initForm();
}

const signatureName = 'Signature'; // Generate signature annotation widget

const form = pdf._root.data.AcroForm;
const fieldId = form.data.Fields.length + 1;
const signatureName = `Signature${fieldId}`;
form.data = {
Type: 'AcroForm',
SigFlags: 3,
Fields: form.data.Fields,
DR: form.data.DR
};
const widget = pdf.ref({
Type: 'Annot',
Subtype: 'Widget',
FT: 'Sig',
Rect: [0, 0, 0, 0],
V: signature,
T: new String(signatureName + (fieldIds.length + 1)),
T: new String(signatureName),
// eslint-disable-line no-new-wrappers
F: 4,
P: pdf.page.dictionary // eslint-disable-line no-underscore-dangle

// F: 4,
P: pdf.page.dictionary
});
pdf.page.dictionary.data.Annots = [widget]; // Include the widget in a page

let form;
pdf.page.annotations.push(widget); // Include the widget in a page

if (!isAcroFormExists) {
// Create a form (with the widget) and link in the _root
form = pdf.ref({
Type: 'AcroForm',
SigFlags: 3,
Fields: [...fieldIds, widget]
});
} else {
// Use existing acroform and extend the fields with newly created widgets
form = pdf.ref({
Type: 'AcroForm',
SigFlags: 3,
Fields: [...fieldIds, widget]
}, acroFormId);
}
form.data.Fields.push(widget);
signature.end();
widget.end(); // form.end() is called by pdfkit itself

pdf._root.data.AcroForm = form;
return {
signature,
form,
Expand Down
3 changes: 2 additions & 1 deletion dist/helpers/pdfkitReferenceMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ class PDFKitReferenceMock extends _abstract_reference.default {
constructor(index, additionalData = undefined) {
super();
this.index = index;
this.data = {};

if (typeof additionalData !== 'undefined') {
Object.assign(this, additionalData);
Object.assign(this.data, additionalData);
}
}

Expand Down
59 changes: 59 additions & 0 deletions dist/helpers/plainAddPlaceholder/getAcroForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"use strict";

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;

var _pdfkitReferenceMock = _interopRequireDefault(require("../pdfkitReferenceMock"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const getAcroForm = pdfBuffer => {
const acroFormPosition = pdfBuffer.lastIndexOf('/Type /AcroForm');
const isAcroFormExists = acroFormPosition > -1;
const data = {
SigFlags: 3,
Fields: [],
DR: {
Font: {}
}
};
let fieldIds = [];
let acroFormId;

if (isAcroFormExists) {
let acroFormStart = acroFormPosition; // 10 is the distance between "/Type /AcroForm" and AcroFrom ID

const charsUntilIdEnd = 10;
const acroFormIdEnd = acroFormPosition - charsUntilIdEnd; // Let's find AcroForm ID by trying to find the "\n" before the ID
// 12 is a enough space to find the "\n" (generally it's 2 or 3, but I'm giving a big space though)

const maxAcroFormIdLength = 12;
let foundAcroFormId = '';

for (let i = charsUntilIdEnd + 1; i < charsUntilIdEnd + maxAcroFormIdLength; i += 1) {
const acroFormIdString = pdfBuffer.slice(acroFormPosition - i, acroFormIdEnd).toString();

if (acroFormIdString[0] === '\n') {
break;
}

foundAcroFormId = acroFormIdString;
acroFormStart = acroFormPosition - i;
}

const pdfSlice = pdfBuffer.slice(acroFormStart);
const acroForm = pdfSlice.slice(0, pdfSlice.indexOf('endobj')).toString();
acroFormId = parseInt(foundAcroFormId);
const acroFormFields = acroForm.slice(acroForm.indexOf('/Fields [') + 9, acroForm.indexOf(']'));
fieldIds = acroFormFields.split(' ').filter((element, index) => index % 3 === 0).map(fieldId => new _pdfkitReferenceMock.default(fieldId, {
id: fieldId
}));
data.Fields = fieldIds;
return new _pdfkitReferenceMock.default(acroFormId, data);
}
};

var _default = getAcroForm;
exports.default = _default;
54 changes: 39 additions & 15 deletions dist/helpers/plainAddPlaceholder/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var _createBufferPageWithAnnotation = _interopRequireDefault(require("./createBu

var _createBufferTrailer = _interopRequireDefault(require("./createBufferTrailer"));

var _getAcroForm = _interopRequireDefault(require("./getAcroForm"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const isContainBufferRootWithAcroform = pdf => {
Expand Down Expand Up @@ -57,31 +59,51 @@ const plainAddPlaceholder = ({
const info = (0, _readPdf.default)(pdf);
const pageRef = (0, _getPageRef.default)(pdf, info);
const pageIndex = (0, _getIndexFromRef.default)(info.xref, pageRef);
const acroForm = (0, _getAcroForm.default)(pdfBuffer);
const addedReferences = new Map();
const references = [];
const dictionary = new _pdfkitReferenceMock.default(pageIndex, {
Annots: []
});
const pdfKitMock = {
ref: (input, additionalIndex) => {
ref: data => {
info.xref.maxIndex += 1;
const index = additionalIndex != null ? additionalIndex : info.xref.maxIndex;
const index = info.xref.maxIndex;
addedReferences.set(index, pdf.length + 1); // + 1 new line

pdf = Buffer.concat([pdf, Buffer.from('\n'), Buffer.from(`${index} 0 obj\n`), Buffer.from(_pdfobject.default.convert(input)), Buffer.from('\nendobj\n')]);
return new _pdfkitReferenceMock.default(info.xref.maxIndex);
const ref = new _pdfkitReferenceMock.default(info.xref.maxIndex, data);
references.push(ref);
return ref;
},
page: {
dictionary: new _pdfkitReferenceMock.default(pageIndex, {
data: {
Annots: []
annotations: {
push(...args) {
dictionary.data.Annots.push(...args);
}
})

},
dictionary
},
_root: {
data: {}
data: {
AcroForm: acroForm
}
},
_acroform: acroForm ? {} : undefined,

initForm() {
this._acroform = {};
const form = this.ref({
Fields: [],
DR: {
Font: {}
}
});
this._root.data.AcroForm = form;
}

};
const {
form,
widget
} = (0, _pdfkitAddPlaceholder.default)({
(0, _pdfkitAddPlaceholder.default)({
pdf: pdfKitMock,
pdfBuffer,
reason,
Expand All @@ -90,15 +112,17 @@ const plainAddPlaceholder = ({
location,
signatureLength
});
pdf = references.reduce((buffer, ref) => Buffer.concat([buffer, Buffer.from('\n'), Buffer.from(`${ref.index} 0 obj\n`), Buffer.from(_pdfobject.default.convert(ref.data)), Buffer.from('\nendobj\n')]), pdf);

if (!isContainBufferRootWithAcroform(pdf)) {
const rootIndex = (0, _getIndexFromRef.default)(info.xref, info.rootRef);
addedReferences.set(rootIndex, pdf.length + 1);
pdf = Buffer.concat([pdf, Buffer.from('\n'), (0, _createBufferRootWithAcroform.default)(pdf, info, form)]);
pdf = Buffer.concat([pdf, Buffer.from('\n'), (0, _createBufferRootWithAcroform.default)(pdf, info, pdfKitMock._root.data.AcroForm)]);
}

addedReferences.set(pageIndex, pdf.length + 1);
pdf = Buffer.concat([pdf, Buffer.from('\n'), (0, _createBufferPageWithAnnotation.default)(pdf, info, pageRef, widget)]);
pdf = Buffer.concat([pdf, Buffer.from('\n'), // probably a nicer way to get the widget - last field in form?
(0, _createBufferPageWithAnnotation.default)(pdf, info, pageRef, pdfKitMock._root.data.AcroForm.data.Fields.pop())]);
pdf = Buffer.concat([pdf, Buffer.from('\n'), (0, _createBufferTrailer.default)(pdf, info, addedReferences)]);
return pdf;
};
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"test:watch": "./node_modules/.bin/jest --watch",
"build": "./node_modules/.bin/babel ./src -d ./dist --ignore \"**/*.test.js\"",
"build:watch": "./node_modules/.bin/babel --watch ./src -d ./dist --ignore \"**/*.test.js\"",
"lint": "./node_modules/.bin/eslint ./src --ext .js",
"precommit": "npm run lint",
"prepush": "npm run test"
},
Expand Down Expand Up @@ -62,6 +63,6 @@
"eslint-plugin-jest": "^22.4.1",
"jest": "^24.5.0",
"node-forge": "^0.10.0",
"pdfkit": "^0.10.0"
"pdfkit": "^0.11.0"
}
}
3 changes: 0 additions & 3 deletions src/helpers.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,6 @@ const createPdf = (params = {
reason: 'I am the author',
...params.placeholder,
});
// Externally end the streams of the created objects.
// PDFKit doesn't know much about them, so it won't .end() them.
Object.keys(refs).forEach(key => refs[key].end());

// Also end the PDFDocument stream.
// See pdf.on('end'... on how it is then converted to Buffer.
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/pdfkit/abstract_reference.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class PDFAbstractReference {
toString() {
throw new Error('Must be implemented by subclasses');
}

end() {
// noop
}
}

export default PDFAbstractReference;
Loading

0 comments on commit 1f8ef4a

Please sign in to comment.