itextsharp - AcroForm 字段值在签署 PDF 文档后消失
itextsharp - AcroForm field value disappears after signing PDF document
给出以下情况:第1方签署pdf,第2方填写AcroForm字段,然后也签署pdf。
问题是,在第二次签名后,表格字段值仅在我单击该字段时出现。表单填写过程和签名过程都发生在追加模式。
签名pdf代码:
using (PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '[=10=]', tempFolder, true))
{
// Creating the signature
SigPadSignature sig = new SigPadSignature(sigInfo, DigestAlgorithms.SHA256);
// Creating the appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = sigInfo.Reason;
appearance.Location = sigInfo.Location;
appearance.Contact = sigInfo.ContactInfo;
appearance.Certificate = chain[0];
appearance.SignatureCreator = sigInfo.SignatureCreator;
appearance.SetVisibleSignature(area.CreateRectangle(), area.PageNumber, fieldName);
appearance.Layer2Text = string.Format("\n{0}\n{1}, {2:g}", sigInfo.SignerName, sigInfo.Location, DateTime.Now);
appearance.Layer2Font = new Font(Font.FontFamily.COURIER, 7.0f);
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
appearance.SignatureGraphic = Image.GetInstance(sigInfo.SignatureImage, System.Drawing.Imaging.ImageFormat.Bmp);
appearance.SignatureEvent = sig;
MakeSignature.SignDetached(appearance, sig, chain, /*crlList*/null, /*ocspClient*/null, /*tsaClient*/null, estimatedSize, CryptoStandard.CMS);
}
填写表单字段代码:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '[=11=]', true))
{
stamper.AcroFields.SetField("fieldname", "test1234");
}
这里有一个例子PDF。如果您在 AdobeReader 中打开它,您可以看到所描述的场景。表单字段值仅在单击该字段时出现。
我做错了什么?有什么建议吗?
问候
简而言之
您实际上在 iTextSharp v5.x 中发现了一个错误(在 iText v5.x 和 OpenPdf 中也是如此)。在此库中以追加模式工作时,必须将原始文件中的每个间接对象标记为 'used' 但 iText(Sharp) 在更改字段外观的上下文中忘记为一个对象这样做。
这个对象不需要是间接的,实际上它更经常是直接的或不存在的而不是间接的;因此,这个错误通常不会产生明显的影响。
您可以通过在设置字段值之前删除其先前形式的对象来解决此问题:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '[=10=]', true))
{
stamper.AcroFields.GetFieldItem("AcroFormField_0").GetWidget(0).Remove(PdfName.AP);
stamper.AcroFields.SetField("AcroFormField_0", "test1234");
}
(实际上一些 null
检查在这里也不会受到伤害...)
详细
设置字段值时,最终会调用AcroFields.SetField(String name, String value, String display, bool saveAppearance)
。如果是文本字段,该字段的新外观将通过以下代码在该方法中更新:
PdfAppearance app = GetAppearance(merged, display, name);
...
PdfDictionary appDic = widget.GetAsDict(PdfName.AP);
if (appDic == null) {
appDic = new PdfDictionary();
widget.Put(PdfName.AP, appDic);
merged.Put(PdfName.AP, appDic);
}
appDic.Put(PdfName.N, app.IndirectReference);
writer.ReleaseTemplate(app);
如果之前没有外观字典,则创建一个新的作为直接对象,一切正常。如果有外观字典作为直接对象,它与 widget
标记为进一步使用相同。但是,如果有一个外观字典 appDict
作为间接对象,则该字典未标记为已使用,因此最终不会保存更改 (appDic.Put(PdfName.N, app.IndirectReference)
),即新外观与该字段无关小部件。
解决方法是将以前存在的外观字典标记为已使用,例如像这样:
if (appDic == null) {
...
} else {
MarkUsed(appDic);
}
通过按照上面的建议先将其删除,iText 会创建一个新的外观字典作为直接对象,因此可以绕过该错误。
那么签名...?
看起来 Adobe Reader 识别出之前的处理器忘记更新外观,因此在 PDF 的最后一个操作中设置了字段值的情况下,它本身也在后台进行更新。
不过,在向该 PDF 添加另一个签名后,Adobe Reader 显然不想再在后台执行此操作。这毕竟会改变签名者在签名时所看到的内容,并且 Adobe 试图防止其软件可能因更改先前签名的数据而受到指责的情况...
给出以下情况:第1方签署pdf,第2方填写AcroForm字段,然后也签署pdf。
问题是,在第二次签名后,表格字段值仅在我单击该字段时出现。表单填写过程和签名过程都发生在追加模式。
签名pdf代码:
using (PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '[=10=]', tempFolder, true))
{
// Creating the signature
SigPadSignature sig = new SigPadSignature(sigInfo, DigestAlgorithms.SHA256);
// Creating the appearance
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.Reason = sigInfo.Reason;
appearance.Location = sigInfo.Location;
appearance.Contact = sigInfo.ContactInfo;
appearance.Certificate = chain[0];
appearance.SignatureCreator = sigInfo.SignatureCreator;
appearance.SetVisibleSignature(area.CreateRectangle(), area.PageNumber, fieldName);
appearance.Layer2Text = string.Format("\n{0}\n{1}, {2:g}", sigInfo.SignerName, sigInfo.Location, DateTime.Now);
appearance.Layer2Font = new Font(Font.FontFamily.COURIER, 7.0f);
appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.GRAPHIC_AND_DESCRIPTION;
appearance.SignatureGraphic = Image.GetInstance(sigInfo.SignatureImage, System.Drawing.Imaging.ImageFormat.Bmp);
appearance.SignatureEvent = sig;
MakeSignature.SignDetached(appearance, sig, chain, /*crlList*/null, /*ocspClient*/null, /*tsaClient*/null, estimatedSize, CryptoStandard.CMS);
}
填写表单字段代码:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '[=11=]', true))
{
stamper.AcroFields.SetField("fieldname", "test1234");
}
这里有一个例子PDF。如果您在 AdobeReader 中打开它,您可以看到所描述的场景。表单字段值仅在单击该字段时出现。
我做错了什么?有什么建议吗?
问候
简而言之
您实际上在 iTextSharp v5.x 中发现了一个错误(在 iText v5.x 和 OpenPdf 中也是如此)。在此库中以追加模式工作时,必须将原始文件中的每个间接对象标记为 'used' 但 iText(Sharp) 在更改字段外观的上下文中忘记为一个对象这样做。
这个对象不需要是间接的,实际上它更经常是直接的或不存在的而不是间接的;因此,这个错误通常不会产生明显的影响。
您可以通过在设置字段值之前删除其先前形式的对象来解决此问题:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '[=10=]', true))
{
stamper.AcroFields.GetFieldItem("AcroFormField_0").GetWidget(0).Remove(PdfName.AP);
stamper.AcroFields.SetField("AcroFormField_0", "test1234");
}
(实际上一些 null
检查在这里也不会受到伤害...)
详细
设置字段值时,最终会调用AcroFields.SetField(String name, String value, String display, bool saveAppearance)
。如果是文本字段,该字段的新外观将通过以下代码在该方法中更新:
PdfAppearance app = GetAppearance(merged, display, name);
...
PdfDictionary appDic = widget.GetAsDict(PdfName.AP);
if (appDic == null) {
appDic = new PdfDictionary();
widget.Put(PdfName.AP, appDic);
merged.Put(PdfName.AP, appDic);
}
appDic.Put(PdfName.N, app.IndirectReference);
writer.ReleaseTemplate(app);
如果之前没有外观字典,则创建一个新的作为直接对象,一切正常。如果有外观字典作为直接对象,它与 widget
标记为进一步使用相同。但是,如果有一个外观字典 appDict
作为间接对象,则该字典未标记为已使用,因此最终不会保存更改 (appDic.Put(PdfName.N, app.IndirectReference)
),即新外观与该字段无关小部件。
解决方法是将以前存在的外观字典标记为已使用,例如像这样:
if (appDic == null) {
...
} else {
MarkUsed(appDic);
}
通过按照上面的建议先将其删除,iText 会创建一个新的外观字典作为直接对象,因此可以绕过该错误。
那么签名...?
看起来 Adobe Reader 识别出之前的处理器忘记更新外观,因此在 PDF 的最后一个操作中设置了字段值的情况下,它本身也在后台进行更新。
不过,在向该 PDF 添加另一个签名后,Adobe Reader 显然不想再在后台执行此操作。这毕竟会改变签名者在签名时所看到的内容,并且 Adobe 试图防止其软件可能因更改先前签名的数据而受到指责的情况...