gpt4 book ai didi

java - 使用pdfbox 2.0.12 java的多个设计?

转载 作者:行者123 更新时间:2023-12-02 01:52:01 39 4
gpt4 key购买 nike

我试图在单个压模上的pdf中添加多个签名。我可以添加多个压模。就我而言,我遇到了错误


  至少一个签名无效。


我想在单个PDF中添加多个有效符号。请帮我。
在图片中,只有一个符号有效,其他符号无效,所以让我做错了

我的代码快照如下

public void getSignOnPdf(Map<Integer, byte[]> PdfSigneture1, List<Long> documentIds, List<String> calTimeStamp,
String originalPdfReadServerPath, String tickImagePath, int serverTime, int pageNumberToInsertStamp,
String name, String location, String reasonForSign, int xCo_ordinates, int yCo_ordinates,
int signatureWidth, int signatureHeight, String pdfPassword, String internal_outputFinalPdfPath)
throws Exception {
String pdfReadServerPath = null;
String l_slash = new String();
String originalPDFPath = new String(originalPdfReadServerPath.trim());

boolean isCorrectPDFOutputPath = false;
String aspOutputPdfServerPath = null;
synchronized (this) {
if ((internal_outputFinalPdfPath != null) && (!internal_outputFinalPdfPath.trim().isEmpty())) {
System.out.println("[" + EsignCommonFuntion.generateTimeStampForLog()
+ "] :1--> outputFinalPdfPath is: " + internal_outputFinalPdfPath);
if (!(new File(internal_outputFinalPdfPath)).isFile()) {
isCorrectPDFOutputPath = true;
aspOutputPdfServerPath = internal_outputFinalPdfPath;
} else {
System.out.println("1--> Please provide directory path for outputFinalPdfPath: "
.concat(String.valueOf(internal_outputFinalPdfPath)));
}
} else {
System.out.println(" 1--> outputFinalPdfPath is empty or null: "
.concat(String.valueOf(internal_outputFinalPdfPath)));
}
}
boolean isPasswordPresent = false;
String pdfPasswordForEncryption;
synchronized (this) {
if ((pdfPassword != null) && (!pdfPassword.trim().isEmpty())) {
pdfPasswordForEncryption = pdfPassword.trim();
isPasswordPresent = true;
} else {
pdfPasswordForEncryption = null;
}
String pdfOriginalName = (new File(originalPDFPath)).getName();
String pdfAbsolutePath = originalPDFPath.substring(0, originalPDFPath.lastIndexOf(l_slash));
if (isPasswordPresent) {
pdfAbsolutePath = getEncryptedPdfName(originalPDFPath, pdfAbsolutePath + l_slash,
pdfPasswordForEncryption, pdfOriginalName);
pdfReadServerPath = new String(pdfAbsolutePath);
} else {
pdfReadServerPath = originalPDFPath;
}
}
ArrayList<String> unSignedFilesList = new ArrayList<String>();

Map<Integer, byte[]> l_PdfSigneture = PdfSigneture1;

int actual_pageNumForStamp = 1;

String pdfFileName = (new File(pdfReadServerPath)).getName();

FileOutputStream fos = null;

String nameToShowInSignature = name;
String locationToShowInSignature = location;
String reasonForSignatureSign = reasonForSign;

PDDocument documentFinal = null;
try {
pdfReadServerPath = pdfReadServerPath.substring(0, pdfReadServerPath.lastIndexOf(l_slash));
System.out.println("inside getSignOnMethod pdfAbsolutePath:".concat(String.valueOf(pdfReadServerPath)));
unSignedFilesList.add(pdfFileName);
System.out.println("inside getSignOnMethod pdfFileName:".concat(String.valueOf(pdfFileName)));

String PDFpath = pdfReadServerPath + l_slash + (String) (unSignedFilesList).get(0);

System.out.println("Inside for PDFpath: ".concat(String.valueOf(PDFpath)));

String finalOutputPdfName = ((String) (unSignedFilesList).get(0)).substring(0,
((String) (unSignedFilesList).get(0)).lastIndexOf(".")) + "_signedFinal.pdf";

File outFile2 = null;

if (isCorrectPDFOutputPath) {
System.out.println("if condition Final signed PDF ouptut Path: " + aspOutputPdfServerPath + l_slash
+ finalOutputPdfName);
outFile2 = new File(aspOutputPdfServerPath + l_slash + finalOutputPdfName);
fos = new FileOutputStream(outFile2);
} else {
outFile2 = new File(pdfReadServerPath + l_slash + outFile2);
fos = new FileOutputStream(outFile2);
}

documentFinal = PDDocument.load(new File(PDFpath));

for (int i = 1; i < 4; i++) {
FileInputStream image2 = new FileInputStream(tickImagePath);

PDSignature pdsignature = new PDSignature();
pdsignature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
pdsignature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);

Calendar cal = GregorianCalendar.getInstance();
SimpleDateFormat l_simpleDateFormater = new SimpleDateFormat("yyyyMMdd_HHmmss");
String timeStamp = (String) calTimeStamp.get(i - 1);

try {
cal.setTime(l_simpleDateFormater.parse(timeStamp));
} catch (ParseException ex) {
ex.printStackTrace();
}

cal.add(12, serverTime);
pdsignature.setSignDate(cal);
documentFinal.setDocumentId((Long) documentIds.get(i - 1));

String dateToShowInSignature = cal.getTime().toString();

Float saveIncrementalObj1 = null;
saveIncrementalObj1 = new Float((float) xCo_ordinates, (float) yCo_ordinates, (float) signatureWidth,
(float) signatureHeight);

PDRectangle rect = getPDRectangle(documentFinal, saveIncrementalObj1, i);
PDVisibleSignDesigner visibleSig;
(visibleSig = new PDVisibleSignDesigner(documentFinal, image2, i)).xAxis(xCo_ordinates)
.yAxis(yCo_ordinates).zoom(-95.0F).signatureFieldName("signature");

PDVisibleSigProperties visibleSignatureProp = new PDVisibleSigProperties();

visibleSignatureProp.signerName("name").signerLocation("location").signatureReason("Security")
.preferredSize(0).page(i - 1).visualSignEnabled(true).setPdVisibleSignature(visibleSig)
.buildSignature();
try {
PdfSigneture = new TreeMap<>();
// PdfSigneture.clear();
PdfSigneture = l_PdfSigneture;

if (visibleSignatureProp.isVisualSignEnabled()) {
this.options = new SignatureOptions();
this.options.setVisualSignature(visibleSignatureProp);
this.options.setPage(visibleSignatureProp.getPage());
this.options.setVisualSignature(
getInputStream(documentFinal, i, rect, tickImagePath, nameToShowInSignature,
locationToShowInSignature, dateToShowInSignature, reasonForSignatureSign));
documentFinal.addSignature(pdsignature, this, this.options);
} else {
documentFinal.addSignature(pdsignature, this);
}
} catch (Exception e) {
e.printStackTrace();
}
}

synchronized (this) {
SaveIncrementalSignObject saveIncrementalSignObject;
(saveIncrementalSignObject = new SaveIncrementalSignObject()).setFos(fos);
saveIncrementalSignObject.setPDDocumentFromFile(documentFinal);

saveIncrementalForSign(saveIncrementalSignObject);
}
} catch (Exception localException2) {
System.out.println("Insidemethod -- Exception block" + localException2.getMessage());
return;
} finally {
fos.flush();
if (fos != null) {
fos.close();
}
documentFinal.close();
}
}

public static synchronized void saveIncrementalForSign(SaveIncrementalSignObject p_SaveIncrementalObj) {
PDDocument documentFinal = null;
try {
(documentFinal = p_SaveIncrementalObj.getPDDocumentFromFile())
.saveIncremental(p_SaveIncrementalObj.getFos());
} catch (Exception e) {
e.printStackTrace();
try {
// documentFinal.close();
return;
} catch (Exception eX) {
eX.printStackTrace();
return;
}
}
}

最佳答案

在评论中,您阐明了要实现的目标:


  我试图将一个签名应用于多个地方。


正如下面第一节中所讨论的,这不是您的代码所要做的:您的代码尝试将多个签名应用于单个修订版本中的每个签名,这在此处是不可能的。

另一方面,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版本中一个接一个地应用,而不是在同一版本中并行使用:



您可以在 this answer中找到一些背景以及从那里引用的文档。

因此,在伪代码中,您要做的是:

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签名容器嵌入其中的签名值字典。


为了执行您的实际任务,


  将一个签名应用于多个地方,


因此,从具有签名外观的多个页面到单个签名容器,似乎有许多选项可供选择:


具有签名可视化的所有页面均指向单个签名字段的相同单个小部件注释,并且带有包含签名容器的值字典。
每个具有签名可视化的页面都指向它们自己的窗口小部件,但是所有窗口小部件都属于同一单个签名字段,并且值字典包含签名容器。
每个具有签名可视化的页面都指向它们自己的窗口小部件,每个窗口小部件都属于一个单独的签名字段,但是所有页面都指向包含签名容器的同一值字典。


现在让我们看一下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。

尽管如此,如果仍要尝试,则可以使用选项3的方法来调整或修补PDFBox以创建具有多个可视化效果的单个签名。

它已经被证明可以用于例如iText,请参阅。 this answer

此外,您共享的样本文档也使用此选项。

概念证明

事实证明,使用PDFBox沿选项3的方式创建多可视化PDF签名非常容易。特别是,比使用iText进行操作要容易得多。 the answer referenced above,因为此处的签名值字典是一个由我创建并处理的对象,而在iText中它是在幕后及时创建的。

所有要做的就是创建一个 PDSignature对象并正常地使用它生成一个签名(使用 PDDocument.addSignature),然后根据需要添加任意多个其他签名字段,并将这些字段的签名值属性设置为单个< cc>对象在开始时创建。

例如。您可以使用这样的方法来添加其他签名字段:

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辅助方法)

(此方法实际上基于pdfbox示例工件中的 PDSignature方法,但是经过了严重简化,现在用于创建实际的签名字段,而不仅仅是从中复制模板。)

这样使用

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测试 CreateVisibleSignature2.createVisualSignatureTemplate

可以在结果文档的每一页上检索带有签名可视化效果的PDF(由于我有点懒,所以还可以看到一个额外的不可见的PDF),但是只有一个实际签名值(假设 testCreateSignatureWithMultipleVisualizations使用 this实现 SignatureInterface方法)。

但是要当心:


byte[] sign(InputStream)方法 PDSignatureField在PDFBox 3.0.0-SNAPSHOT中已被弃用。您最终可能必须使用更多低级技术来注入 setSignature对象。
PDF规范团队不需要这种多可视化签名。最终他们有可能被禁止。

关于java - 使用pdfbox 2.0.12 java的多个设计?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52829507/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com