给出以下方案:第1方签名pdf,第2方填写AcroForm字段,然后也对pdf签名。
问题是,在第二次签名后,仅出现表单字段值如果我单击该字段。表单填写过程和签名过程都以追加方式进行。
签名pdf代码:
using (PdfStamper stamper = PdfStamper.CreateSignature(reader, os, '\0', 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, '\0', true))
{
stamper.AcroFields.SetField("fieldname", "test1234");
}
这里是一个示例PDF。如果在AdobeReader中打开它,则可以看到描述的szenario。表单字段值仅在单击字段时出现。
我在做什么错?有什么建议吗?
问候
答案 0 :(得分:0)
您实际上在iTextSharp v5.x(也在iText v5.x和OpenPdf中)发现了一个错误。在此库中以添加模式工作时,必须将原始文件中的每个间接对象都标记为“已使用”,但iText(Sharp)忘记在更改字段外观的情况下对一个对象这样做。
此对象不必是间接的,实际上它比间接的更常见是直接的或不存在的;因此,此错误不会经常产生可见的影响。
您可以通过以下方法解决此问题:在设置设置字段值之前,先删除该对象的先前形式:
using (PdfStamper stamper = new PdfStamper(reader, outStrm, '\0', 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试图防止这种情况,因为这种情况可能被指责其软件更改了先前签名的数据...