I was trying to add multiple signatures in a single pdf on stamper. I am able to add multiple stampers. In my case on one, I was getting the error
at leas
In a comment you clarified what you want to achieve:
I tried to applying one signature to multiple place.
As discussed in the first section below this is not what your code does: your code attempts to apply multiple signatures to one place each in a single revision which is impossible as also explained there.
Applying a single signature to multiple places in a single revision, on the other hand, is not desired by the PDF specification team and some approaches to implement this have been made invalid by the specification, but it is possible as explained in the second section below.
You appear to try to apply multiple signatures in one pass:
if (isPasswordPresent) {
documentFinal = PDDocument.load(new File(PDFpath), pdfPasswordForEncryption);
} else {
documentFinal = PDDocument.load(new File(PDFpath));
}
for (int i = 1; i < 4; i++) {
FileInputStream image2 = new FileInputStream(tickImagePath);
PDSignature pdsignature = new PDSignature();
[...]
try {
[...]
if (visibleSignatureProp.isVisualSignEnabled()) {
[...]
documentFinal.addSignature(pdsignature, this, this.options);
} else {
documentFinal.addSignature(pdsignature, this);
}
} catch (Exception e) {
System.out.println("Inside getSignOnPdf sub exception block at addSignature:" + e + "error :" + e.getMessage());
e.printStackTrace();
}
}
synchronized (this) {
[...]
saveIncrementalForSign(saveIncrementalSignObject);
}
This cannot work.
In PDFs multiple signatures are applied one after the other in separate PDF revisions, not all in parallel in the same revision:
You can find some backgrounds in this answer and the documents referenced from there.
Thus, in pseudo code what you have to do instead is:
for (int i = 1; i < 4; i++) {
load current version of the PDF;
apply the i'th signature;
save and sign as new current version of the PDF;
}
The method name PDDocument.addSignature
might be a little misleading here as it might be assumed to imply that multiple signatures may be added. This is not the case; all signatures will be created as signature fields with their widgets but only the field of the last added PDSignature
will actually be signed, so only this last added signature field will actually have a sensible value.
@Tilman - there probably should be a test in PDDocument.addSignature
throwing an exception if a signature already has been added since loading the document.
The path of PDF objects from a signature visualization on a PDF page to the actual signature (the CMS signature container in case of CMS based subfilters) is not immediate. Instead we have
For the implementation of your actual task,
applying one signature to multiple places,
therefore, there appear to be a number of options to get from multiple pages with signature appearances to the single signature container:
Let's now look at the PDF specification ISO 32000-2. First of all it warns against having single signatures with multiple visualizations:
The location of a signature within a document can have a bearing on its legal meaning. [...]
If more than one location is associated with a signature, the meaning can become ambiguous.
(ISO 32000-2, section 12.7.5.5 "Signature fields")
Consequentially, the specification attempts to forbid single signatures with multiple visualizations:
A given annotation dictionary shall be referenced from the Annots array of only one page.
(ISO 32000-2, section 12.5.2 "Annotation dictionaries")
This forbids option 1 above.
signature fields shall never refer to more than one annotation
(ISO 32000-2, section 12.7.5.5 "Signature fields")
This forbids option 2.
Apparently, though, option 3 is not explicitly forbidden. For generic form fields value object sharing is even explicitly allowed as the form field value is inheritable!
Thus, strictly speaking creating signatures with multiple visualizations is possible using option 3.
Please be aware, though, that it clearly was not intended by the PDF specification team to allow them, it most likely was an oversight. Thus, you have to reckon that some upcoming corrigenda to the specification will eventually forbid option 3, too.
If you want to try nonetheless, it should be possible to tweak or patch PDFBox to create single signatures with multiple visualizations using the approach of option 3.
It has already proven possible for e.g. iText, cf. this answer.
Furthermore, the sample document you shared makes use of this option.
As it turns out, it is pretty easy to create a multi-visualization PDF signature using PDFBox along the lines of option 3. In particular it is easier than doing this with iText, cf. the answer referenced above, because the signature value dictionary here is an object one creates and handles oneself while in iText it is created under the hood and just in time.
All one has to do is to create one PDSignature
object and generate one signature with it normally (using PDDocument.addSignature
) and then add as many other signature fields as one wants, setting the signature value properties of those fields to the single PDSignature
object create at the start.
E.g. you can use a method like this to add additional signature fields:
void addSignatureField(PDDocument pdDocument, PDPage pdPage, PDRectangle rectangle, PDSignature signature) throws IOException {
PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
List<PDField> acroFormFields = acroForm.getFields();
PDSignatureField signatureField = new PDSignatureField(acroForm);
signatureField.setSignature(signature);
PDAnnotationWidget widget = signatureField.getWidgets().get(0);
acroFormFields.add(signatureField);
widget.setRectangle(rectangle);
widget.setPage(pdPage);
// from PDVisualSigBuilder.createHolderForm()
PDStream stream = new PDStream(pdDocument);
PDFormXObject form = new PDFormXObject(stream);
PDResources res = new PDResources();
form.setResources(res);
form.setFormType(1);
PDRectangle bbox = new PDRectangle(rectangle.getWidth(), rectangle.getHeight());
float height = bbox.getHeight();
form.setBBox(bbox);
PDFont font = PDType1Font.HELVETICA_BOLD;
// from PDVisualSigBuilder.createAppearanceDictionary()
PDAppearanceDictionary appearance = new PDAppearanceDictionary();
appearance.getCOSObject().setDirect(true);
PDAppearanceStream appearanceStream = new PDAppearanceStream(form.getCOSObject());
appearance.setNormalAppearance(appearanceStream);
widget.setAppearance(appearance);
try (PDPageContentStream cs = new PDPageContentStream(pdDocument, appearanceStream))
{
// show background (just for debugging, to see the rect size + position)
cs.setNonStrokingColor(Color.yellow);
cs.addRect(-5000, -5000, 10000, 10000);
cs.fill();
float fontSize = 10;
float leading = fontSize * 1.5f;
cs.beginText();
cs.setFont(font, fontSize);
cs.setNonStrokingColor(Color.black);
cs.newLineAtOffset(fontSize, height - leading);
cs.setLeading(leading);
cs.showText("Signature text");
cs.newLine();
cs.showText("some additional Information");
cs.newLine();
cs.showText("let's keep talking");
cs.endText();
}
pdPage.getAnnotations().add(widget);
COSDictionary pageTreeObject = pdPage.getCOSObject();
while (pageTreeObject != null) {
pageTreeObject.setNeedToBeUpdated(true);
pageTreeObject = (COSDictionary) pageTreeObject.getDictionaryObject(COSName.PARENT);
}
}
(CreateMultipleVisualizations helper method)
(This method actually is based on the CreateVisibleSignature2.createVisualSignatureTemplate
method from the pdfbox examples artifact but severely simplified and now used to create the actual signature fields, not merely a template to copy from.)
Used like this
try ( InputStream resource = PDF_SOURCE_STREAM;
OutputStream result = PDF_TARGET_STREAM;
PDDocument pdDocument = PDDocument.load(resource) )
{
PDAcroForm acroForm = pdDocument.getDocumentCatalog().getAcroForm();
if (acroForm == null) {
pdDocument.getDocumentCatalog().setAcroForm(acroForm = new PDAcroForm(pdDocument));
}
acroForm.setSignaturesExist(true);
acroForm.setAppendOnly(true);
acroForm.getCOSObject().setDirect(true);
PDRectangle rectangle = new PDRectangle(100, 600, 300, 100);
PDSignature signature = new PDSignature();
signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
signature.setName("Example User");
signature.setLocation("Los Angeles, CA");
signature.setReason("Testing");
signature.setSignDate(Calendar.getInstance());
pdDocument.addSignature(signature, this);
for (PDPage pdPage : pdDocument.getPages()) {
addSignatureField(pdDocument, pdPage, rectangle, signature);
}
pdDocument.saveIncremental(result);
}
(CreateMultipleVisualizations test testCreateSignatureWithMultipleVisualizations
)
one retrieves a PDF with a signature visualization on each page of the result document (and an extra invisible one because I was a bit lazy) but only a single actual signature value (given that this
implements SignatureInterface
with the byte[] sign(InputStream)
method).
Beware, though:
PDSignatureField
method setSignature
has been deprecated in PDFBox 3.0.0-SNAPSHOT. You might eventually have to inject the PDSignature
object using more low-level techniques.