Closed ssleptsov closed 6 years ago
Hello @ssleptsov. This is certainly possible, as pdf-lib
provides low level access to pdf objects. However, there is not currently a nice high level API for manipulating annotations or acroform objects, so you'll have to put up with some verbose boilerplate for now :smile:
Here's how to do it:
const pdfBytes = /* A Uint8Array containing the PDF you wish to modify */
const pdfDoc = PDFDocumentFactory.load(pdfBytes);
const [FontHelvetica] = pdfDoc.embedStandardFont('Helvetica');
// Define the signature dictionary (Detailed in Table 252 of PDF specification)
// Note that the dict below isn't actually valid. I just made up its `ByteRange`
// and `Contents` entries. It's just an example.
const signatureDict = PDFDictionary.from({
Type: PDFName.from('Sig'),
Filter: PDFName.from('Adobe.PPKLite'),
SubFilter: PDFName.from('adbe.pkcs7.detached'),
ByteRange: PDFArray.fromArray([
PDFNumber.fromNumber(0),
PDFNumber.fromNumber(0),
PDFNumber.fromNumber(0),
PDFNumber.fromNumber(0),
], pdfDoc.index),
Contents: PDFHexString.fromString('00000000000'),
Reason: PDFString.fromString('We need your signature for reasons...'),
// M: PDFString.fromString('D:YYYYMMDDHHmmSSOHH'mm')
// See section "7.9.4 Dates" of the PDF specification for details on the format of date strings
}, pdfDoc.index);
// Define a content stream that defines how the signature field should appear
// on the PDF.
//
// Note: This isn't necessary if you define the signature widget's "Rect" entry
// as [0, 0, 0, 0] like in the snippet you provided. But I've implemented it
// just in case you (or others) might find it useful.
const sigAppearanceStream = PDFContentStream.of(
PDFDictionary.from({
Type: PDFName.from('XObject'),
Subtype: PDFName.from('Form'),
BBox: PDFArray.fromArray([
PDFNumber.fromNumber(0),
PDFNumber.fromNumber(0),
PDFNumber.fromNumber(200),
PDFNumber.fromNumber(50),
], pdfDoc.index),
Resources: PDFDictionary.from({
Font: PDFDictionary.from({
Helvetica: FontHelvetica
}, pdfDoc.index)
}, pdfDoc.index),
}, pdfDoc.index),
drawRectangle({
x: 0,
y: 0,
width: 200,
height: 50,
colorRgb: [0.95, 0.95, 0.95],
borderWidth: 3,
borderColorRgb: [0, 0, 0],
}),
drawText('Sign Here', {
x: 10,
y: 15,
font: 'Helvetica',
size: 30,
colorRgb: [0.5, 0.5, 0.5],
}),
drawRectangle({
x: 4,
y: 4,
width: 192,
height: 2,
colorRgb: [0.5, 0.5, 0.5],
}),
);
const sigAppearanceStreamRef = pdfDoc.register(sigAppearanceStream);
// Define the signature widget annotation
const widgetDict = PDFDictionary.from({
Type: PDFName.from('Annot'),
Subtype: PDFName.from('Widget'),
FT: PDFName.from('Sig'),
Rect: PDFArray.fromArray([
PDFNumber.fromNumber(50),
PDFNumber.fromNumber(50),
PDFNumber.fromNumber(300),
PDFNumber.fromNumber(100),
], pdfDoc.index),
// Uncomment this if you've created a valid signatureDict
// V: signatureDict,
T: PDFString.fromString('Signature1'),
F: PDFNumber.fromNumber(4),
P: (pdfDoc.catalog.Pages.get('Kids') as PDFArray).get(0),
AP: PDFDictionary.from({
N: sigAppearanceStreamRef,
}, pdfDoc.index),
}, pdfDoc.index);
const widgetDictRef = pdfDoc.register(widgetDict);
// Add our signature widget to the first page
const pages = pdfDoc.getPages();
pages[0].set(
'Annots',
PDFArray.fromArray([widgetDictRef], pdfDoc.index),
);
// Create an AcroForm object containing our signature widget
const formDict = PDFDictionary.from({
SigFlags: PDFNumber.fromNumber(3),
Fields: PDFArray.fromArray([widgetDictRef], pdfDoc.index),
}, pdfDoc.index);
pdfDoc.catalog.set('AcroForm', formDict);
const modifiedPdfBytes = PDFDocumentWriter.saveToBytes(pdfDoc);
Here's a sample PDF that I modified using the script above (notice the "Sign Here" field at the bottom of the first page): pdf-with-signature-field-unsigned.pdf.
And here's the original pdf.
Please let me know if this is what you're looking for, or if you need any additional help with pdf-lib
!
wow, that's awesome! Thanks for your help and the great library!
You bet!
(I updated the pdf-with-signature-field-unsigned.pdf
file - I noticed I had attached the wrong one).
Does this work for filling form fields? I think I do not know PDFs well enough yet.
Yes, it should be able to fill form fields. If you can share a document that you'd like to fill out, I can provide some sample code that does so. If not, I can provide an example of filling out a form field with one of the repo's test PDFs, if you let me know which types of form fields you're working with.
@pspeter3 Sorry I haven’t had a chance to tackle this the past couple of days. I’ll try to write up an example script this evening. If you’re able to submit a PR, that’d be great!
Thanks! As an example, thinking about how to form fill these http://dnd.wizards.com/articles/features/character_sheets. I would be happy to submit a PR with an example of a form fillable document as a thank you for your help. This project is great, thank you for building it.
— You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub https://github.com/Hopding/pdf-lib/issues/39#issuecomment-437608424, or mute the thread https://github.com/notifications/unsubscribe-auth/AJ301KMDCv1QQMgDXaBYZAsXIryyNOe4ks5utx5TgaJpZM4Xx1vf .
No, worries. I think this project is great!
On Tue, Nov 13, 2018, 5:57 AM Andrew Dillon <notifications@github.com wrote:
Sorry I haven’t had a chance to tackle this the past couple of days. I’ll try to write up an example script this evening. If you’re able to submit a PR, that’d be great!
- Andrew On Sat, Nov 10, 2018 at 12:43 PM Phips Peter notifications@github.com wrote:
Thanks! As an example, thinking about how to form fill these http://dnd.wizards.com/articles/features/character_sheets. I would be happy to submit a PR with an example of a form fillable document as a thank you for your help. This project is great, thank you for building it.
— You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub https://github.com/Hopding/pdf-lib/issues/39#issuecomment-437608424, or mute the thread < https://github.com/notifications/unsubscribe-auth/AJ301KMDCv1QQMgDXaBYZAsXIryyNOe4ks5utx5TgaJpZM4Xx1vf
.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/Hopding/pdf-lib/issues/39#issuecomment-438274647, or mute the thread https://github.com/notifications/unsubscribe-auth/AAlF1fAgu_gGvcE9JX6piABrLfZJk4A3ks5uus_cgaJpZM4Xx1vf .
@pspeter3 I created an example script that fills out a DoD character sheet here: https://github.com/Hopding/pdf-lib/issues/48.
My apologies for not getting around to this sooner. Had to deal with some fiascos at work this past week.
wow, that's awesome! Thanks for your help and the great library!
Hey ! I am trying to signed my existing pdf file which i have generated using filling form with hummusjs. But i was not able to set default ByteRange for signature placeholder. I am getting this error
Could not find ByteRange placeholder: /ByteRange [0 /** /** /**]
Can you please help me get this done.
Thanks in advance.
Hello @john-attrium-204!
I need more information in order to help you with this. Can you please provide a code sample and a PDF that I can use to reproduce the issue?
HI, I have been trying to put a signature mark on the pdf, but using this library I modify the PDF with the drawText
function, but I realize that it removes the trailer
from the PDF Buffer. Then I want to sign and he won't let me, because he can't find the trailer
.
To better understand what I am saying, I provide the library with which I am signing and an pull-request
where I am occupying pdf-lib
.
I hope you understand me and help me.
node-signpdf
, here I explain a bit the problem that is happening to me with pdf-lib
According to the example you proposed, you occupy functions that do not exist in pdf-lib
, such as:
And you talk about a 252 table of the PDF specification that I can't find.
@therpobinski I am pretty sure the original snippet in this issue that @ssleptsov posted is a piece of code from the node-signpdf package. So he was trying to do the same thing you are.
What @Hopding gave him is a PDF with a placeholder for signature, so the first part of the task is done. Since the signing code does not care about trailers I think it just worked from here on. @ssleptsov should confirm if that worked for him. But, yes, this is a great snippet that, if @Hopding allows, we can include as a pdflibAddPlaceholder
helper in our lib.
I think @john-attrium-204 was dealing with the same thing.
Sorry @Hopding. Just saw we are commenting on a closed issue. And just because it's my first time to write in your lib, I want to congratulate you on it. I haven't had the time to play around with it too much but it looks awesome from what I see. Awesome work! Awesome share!
@therpobinski I am pretty sure the original snippet in this issue that @ssleptsov posted is a piece of code from the node-signpdf package. So he was trying to do the same thing you are. What @Hopding gave him is a PDF with a placeholder for signature, so the first part of the task is done. Since the signing code does not care about trailers I think it just worked from here on. @ssleptsov should confirm if that worked for him. But, yes, this is a great snippet that, if @Hopding allows, we can include as a
pdflibAddPlaceholder
helper in our lib.I think @john-attrium-204 was dealing with the same thing.
Sorry @Hopding. Just saw we are commenting on a closed issue. And just because it's my first time to write in your lib, I want to congratulate you on it. I haven't had the time to play around with it too much but it looks awesome from what I see. Awesome work! Awesome share!
It will be great if both libraries join hands - both pdf-lib and node-signpdf libraries would offer signing of new and existing pdfs. @Hopding and @vbuch - adding a pdflibAddPlaceholder in addition to pdfkit and plan one would surely resolve a major missing feature request.
wow, that's awesome! Thanks for your help and the great library!
Hey ! I am trying to signed my existing pdf file which i have generated using filling form with hummusjs. But i was not able to set default ByteRange for signature placeholder. I am getting this error
Could not find ByteRange placeholder: /ByteRange [0 /** /** /**]
Can you please help me get this done.
Thanks in advance.
Hi, Hello, I have the same problem and I think I know what it is, I am reviewing it and I see that the placeholder has never been embedded in the PDF, so signing the document does not find the ByteRange and the error occurs. Have you been able to find a solution? I doubt that the example that @Hopding gave us is working, I leave you my codecommit so you can review it and see if we can find a solution.
Hello @ssleptsov. This is certainly possible, as
pdf-lib
provides low level access to pdf objects. However, there is not currently a nice high level API for manipulating annotations or acroform objects, so you'll have to put up with some verbose boilerplate for now 😄Here's how to do it:
const pdfBytes = /* A Uint8Array containing the PDF you wish to modify */ const pdfDoc = PDFDocumentFactory.load(pdfBytes); const [FontHelvetica] = pdfDoc.embedStandardFont('Helvetica'); // Define the signature dictionary (Detailed in Table 252 of PDF specification) // Note that the dict below isn't actually valid. I just made up its `ByteRange` // and `Contents` entries. It's just an example. const signatureDict = PDFDictionary.from({ Type: PDFName.from('Sig'), Filter: PDFName.from('Adobe.PPKLite'), SubFilter: PDFName.from('adbe.pkcs7.detached'), ByteRange: PDFArray.fromArray([ PDFNumber.fromNumber(0), PDFNumber.fromNumber(0), PDFNumber.fromNumber(0), PDFNumber.fromNumber(0), ], pdfDoc.index), Contents: PDFHexString.fromString('00000000000'), Reason: PDFString.fromString('We need your signature for reasons...'), // M: PDFString.fromString('D:YYYYMMDDHHmmSSOHH'mm') // See section "7.9.4 Dates" of the PDF specification for details on the format of date strings }, pdfDoc.index); // Define a content stream that defines how the signature field should appear // on the PDF. // // Note: This isn't necessary if you define the signature widget's "Rect" entry // as [0, 0, 0, 0] like in the snippet you provided. But I've implemented it // just in case you (or others) might find it useful. const sigAppearanceStream = PDFContentStream.of( PDFDictionary.from({ Type: PDFName.from('XObject'), Subtype: PDFName.from('Form'), BBox: PDFArray.fromArray([ PDFNumber.fromNumber(0), PDFNumber.fromNumber(0), PDFNumber.fromNumber(200), PDFNumber.fromNumber(50), ], pdfDoc.index), Resources: PDFDictionary.from({ Font: PDFDictionary.from({ Helvetica: FontHelvetica }, pdfDoc.index) }, pdfDoc.index), }, pdfDoc.index), drawRectangle({ x: 0, y: 0, width: 200, height: 50, colorRgb: [0.95, 0.95, 0.95], borderWidth: 3, borderColorRgb: [0, 0, 0], }), drawText('Sign Here', { x: 10, y: 15, font: 'Helvetica', size: 30, colorRgb: [0.5, 0.5, 0.5], }), drawRectangle({ x: 4, y: 4, width: 192, height: 2, colorRgb: [0.5, 0.5, 0.5], }), ); const sigAppearanceStreamRef = pdfDoc.register(sigAppearanceStream); // Define the signature widget annotation const widgetDict = PDFDictionary.from({ Type: PDFName.from('Annot'), Subtype: PDFName.from('Widget'), FT: PDFName.from('Sig'), Rect: PDFArray.fromArray([ PDFNumber.fromNumber(50), PDFNumber.fromNumber(50), PDFNumber.fromNumber(300), PDFNumber.fromNumber(100), ], pdfDoc.index), // Uncomment this if you've created a valid signatureDict // V: signatureDict, T: PDFString.fromString('Signature1'), F: PDFNumber.fromNumber(4), P: (pdfDoc.catalog.Pages.get('Kids') as PDFArray).get(0), AP: PDFDictionary.from({ N: sigAppearanceStreamRef, }, pdfDoc.index), }, pdfDoc.index); const widgetDictRef = pdfDoc.register(widgetDict); // Add our signature widget to the first page const pages = pdfDoc.getPages(); pages[0].set( 'Annots', PDFArray.fromArray([widgetDictRef], pdfDoc.index), ); // Create an AcroForm object containing our signature widget const formDict = PDFDictionary.from({ SigFlags: PDFNumber.fromNumber(3), Fields: PDFArray.fromArray([widgetDictRef], pdfDoc.index), }, pdfDoc.index); pdfDoc.catalog.set('AcroForm', formDict); const modifiedPdfBytes = PDFDocumentWriter.saveToBytes(pdfDoc);
Here's a sample PDF that I modified using the script above (notice the "Sign Here" field at the bottom of the first page): pdf-with-signature-field-unsigned.pdf.
And here's the original pdf.
Please let me know if this is what you're looking for, or if you need any additional help with
pdf-lib
!
Hi Andrew. Is it possible to have this code adapted to the latest version? Well, I try to use it and it gives me multiple errors. Thank you
@brasycad @Hopding Would also love to see this adapted to the latest version.
@therpobinski @brasycad @vemundeldegard Please see https://github.com/Hopding/pdf-lib/issues/112#issuecomment-569085380. I've updated my example for version 1.3.0
.
Hey, is it possible to get/set Annot or Widget for existing pdf file? I'm trying to replace this pdfkit code with your library
Thanks!