В комментарии вы уточнили, чего хотите достичь:
Я пытался применить одну подпись в нескольких местах.
Как обсуждалось в первом разделе ниже,это не то, что делает ваш код: ваш код пытается применить несколько сигнатур к одному месту в одной ревизии , что невозможно, как объяснено там.
Применение единственной подписи к нескольким местам в единственной ревизии , с другой стороны, нежелательно для команды спецификации PDF и некоторых подходовчтобы реализовать это, было сделано недействительным по спецификации, но это возможно, как объяснено во втором разделе ниже.
Ваш подход и почему он не может работать
Вы, кажется, пытаетесь применить несколькоподписи за один проход:
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);
}
Это не может работать.
В PDF-файлах несколько подписей применяются одна за другой в отдельных редакциях PDF, а не все параллельно вта же ревизия:
Вы можете найти некоторые фоны в этом ответе и документах, на которые есть ссылки.
Таким образом, в псевдокодевместо этого вам нужно сделать следующее:
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;
}
Имя метода PDDocument.addSignature
может немного ввести в заблуждение, поскольку можно предположить, что можно добавить несколько подписей.Это не вариант;все подписи будут созданы как поля подписи со своими виджетами, но только поле последнего добавленного PDSignature
будет подписано, поэтому только это последнее добавленное поле подписи будет иметь разумное значение.
@ Tilman -PDDocument.addSignature
, вероятно, должен быть тест, генерирующий исключение, если подпись уже была добавлена с момента загрузки документа.
Обсуждение вашей фактической задачи
Путь объектов PDF изВизуализация подписи на странице PDF для фактической подписи (контейнер подписи CMS в случае субфильтров на основе CMS) не является немедленной.Вместо этого у нас есть
- страница PDF в ее аннотациях, ссылающаяся на
- виджет поля подписи (визуализация подписи), принадлежащий
- поле подписи, ссылающееся на
- словарь значений подписи, в который встроен контейнер подписи CMS.
Для реализации фактической задачи
применение одной подписи к нескольким местам,
, следовательно, существует несколько вариантов получения с нескольких страниц с отображением подписи в контейнере с единой подписью:
- Все страницы с визуализациями подписи, указывающими на одно и то жеаннотация одного виджета поля с одной подписью со словарем значений, содержащим контейнер для подписи.
- Каждая страница с визуализациями подписей, указывающими на их собственный виджет, но все виджеты, принадлежащие к одному полю с одной подписью, со словарем значений, содержащимКонтейнер для подписи.
- Eкаждая страница с визуализациями подписи, указывающими на собственный виджет, каждый виджет принадлежит отдельному полю подписи, но все они указывают на один и тот же словарь значений, содержащий контейнер подписи.
Давайте теперь посмотрим на PDFспецификация ISO 32000-2.Прежде всего, он предостерегает от использования единой подписи с несколькими визуализациями:
Расположение подписи в документе может иметь отношение к ее юридическому значению.[...]
Если с подписью связано более одного местоположения, значение может стать неоднозначным.
(ISO 32000-2, раздел 12.7.5.5«Поля подписи»)
Соответственно, в спецификации делается попытка запретить отдельные подписи с несколькими визуализациями:
На данный словарь аннотаций следует ссылаться из Annots массив только одной страницы.
(ISO 32000-2, раздел 12.5.2 «Словари аннотаций»)
Это запрещает вышеуказанный вариант 1.
поля подписи никогда не должны ссылатьсяк более чем одной аннотации
(ISO 32000-2, раздел 12.7.5.5 «Поля подписи»)
Это запрещает вариант 2.
Очевидно, что вариант 3 явно не запрещен.Для общих полей формы совместное использование объекта значения даже явно разрешено, поскольку значение поля формы является наследуемым!
Таким образом, строго говоря, создание подписей с несколькими визуализациями возможно с использованием опции 3.
Обратите внимание,хотя команда разработчиков спецификаций PDF явно не собиралась разрешать их, скорее всего, это был недосмотр.Таким образом, вы должны учитывать, что некоторые будущие исправления к спецификации в конечном итоге также будут запрещать вариант 3.
Если вы все же хотите попробовать, должна быть возможность настроить или исправить PDFBox для создания отдельных подписей с несколькими визуализациямииспользуя подход варианта 3.
Это уже доказано возможным, например, для iText, ср. этот ответ .
Более того, образец документа, которым вы поделились, использует эту опцию.
Подтверждение концепции
Как оказалось, ондовольно просто создать мульти-визуализацию PDF-подписи с помощью PDFBox в соответствии с вариантом 3. В частности, это проще, чем делать это с помощью iText, ср. ответ, указанный выше , потому что словарь значений сигнатур здесь - это объект, который человек создает и обрабатывает самостоятельно, тогда как в iText он создается под капотом и как раз вовремя.
Все, что нужно сделатьсостоит в том, чтобы создать один объект PDSignature
и сгенерировать с ним одну сигнатуру (используя PDDocument.addSignature
), а затем добавить столько других полей сигнатур, сколько нужно, установив свойства значения подписи этих полей в один объект PDSignature
, созданный вначало.
Например, вы можете использовать такой метод, чтобы добавить дополнительные поля подписи:
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 вспомогательный метод)
(на самом деле этот метод основан на методе CreateVisibleSignature2.createVisualSignatureTemplate
из артефакта примеров pdfbox, но сильно упрощен и теперь используется для создания полей действительной подписи, а не просто шаблона для копирования.)
Используется следующим образом:
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
)
каждый получает PDFс визуализацией подписи на каждой странице итогового документа (и дополнительной невидимой, потому что я немного ленив), но только с одним фактическим значением подписи (учитывая, что this
реализует SignatureInterface
с помощью метода byte[] sign(InputStream)
).
Осторожно, хотя:
- Метод
PDSignatureField
setSignature
в PDFBox 3.0.0-SNAPSHOT устарел.В конечном итоге вам, возможно, придется внедрить объект PDSignature
, используя более низкоуровневые методы. - Этот вид мульти-визуализации не нужен командам спецификации PDF.Скорее всего, в конечном итоге они будут запрещены.