首先在Acrobat中修改PDF AcroForm字段,然后在iText7中变为空白

首先在Acrobat中修改PDF AcroForm字段,然后在iText7中变为空白,pdf,itext,acrobat,itext7,pdf-form,Pdf,Itext,Acrobat,Itext7,Pdf Form,我正在使用iText7生成一些带有可编辑表单字段的PDF文档。 “我的代码”使用以下代码将字段添加到PDF文档中: @Override public void draw(DrawContext drawContext) { super.draw(drawContext); PdfAcroForm form = PdfAcroForm.getAcroForm(drawContext.getDocument(), true); PdfFormField field = is

我正在使用iText7生成一些带有可编辑表单字段的PDF文档。 “我的代码”使用以下代码将字段添加到PDF文档中:

@Override
public void draw(DrawContext drawContext) {
    super.draw(drawContext);

    PdfAcroForm form = PdfAcroForm.getAcroForm(drawContext.getDocument(), true);
    PdfFormField field = isMultiline
            ?   PdfFormField.createMultilineText(drawContext.getDocument(), getOccupiedAreaBBox(), fieldName, "")
            :   PdfFormField.createText(drawContext.getDocument(), getOccupiedAreaBBox(), fieldName, "");
    field.setFontSize(defaultFontSize - 1);
    form.addField(field);
}
这是iText自定义渲染器类的
draw
方法
生成的PDF文档按预期工作。我可以在AdobeAcrobatReader中打开它,填写可编辑字段,保存它,重新打开它,等等

我的要求之一是允许以编程方式展平表单(显然是在人工用户填写表单之后),以便此后禁用编辑

我使用以下代码执行此操作:

PdfDocument pdf = ... // Read a PDF stream and get the document object
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, false);
form.flattenFields();
pdf.close()
现在的问题是,当我用PDF阅读器打开展开的PDF时,字段是空的。

我进行了一些调试,看起来只有当我尝试展平使用Adobe Reader(版本XI)编辑(并保存)的字段时,才会出现此问题。 如果我以编程方式编辑字段,例如使用iText的
PdfFormField#setValue(String)
方法,则展开的PDF可以很好地呈现值。

Adobe Reader似乎正在设置表单字段的某些属性,以防止它们被正确展平。。。真的是这样吗?有什么办法吗
多谢各位

-----------编辑-----------------

显然,这个问题是由Adobe Reader引起的:出于某种原因,它保存字段定义时没有
/XObject
子类型,iText7会在
form.flattFields()
方法中检查该子类型
这是Eclipse调试器中字段字符串的外观:

由iText7生成:

由Acrobat Reader保存:

如您所见,
/Subtype
部分缺失。无论何时尝试禁用表单字段,例如
PdfAcroForm#flattfields()
PdfFormField#setReadOnly(布尔值)
,iText都会尝试使用以下代码在
PdfCanvas
上重新绘制其内容:

  if(xObject != null && xObject.getPdfObject().get(PdfName.Subtype) != null) {
    [...]
    canvas.addXObject(xObject, ...);
  }
它被跳过,因为Reader字段不再是
/XObject

我目前的解决方法是用新字段替换该字段:

Map<String,PdfFormField> fields = form.getFormFields();
Set<String> keys = new HashSet<>(fields.keySet());      // avoids concurrent modifications

for(String fieldName : keys) {
    PdfFormField field = fields.get(fieldName);
    PdfDictionary fieldObject = field.getPdfObject();

    PdfFormField newField = field.isMultiline() 
            ? PdfFormField.createMultilineText(pdf, fieldObject.getAsRectangle(PdfName.Rect), fieldName, 
                    field.getValueAsString(), defaultFont, defaultFontSize)
            : PdfFormField.createText(pdf, fieldObject.getAsRectangle(PdfName.Rect), fieldName, 
                    field.getValueAsString(), defaultFont, defaultFontSize);

    form.replaceField(fieldName, newField);
}

form.flattenFields();
Map fields=form.getFormFields();
Set keys=new HashSet(fields.keySet());//避免并发修改
for(字符串字段名:键){
PdfFormField=fields.get(fieldName);
PdfDictionary fieldObject=field.getPdfObject();
PdfFormField newField=field.isMultiline()
?PdfFormField.createMultilineText(pdf,fieldObject.getAsRectangle(PdfName.Rect)),fieldName,
field.getValueAsString(),defaultFont,defaultFontSize)
:PdfFormField.createText(pdf,fieldObject.getAsRectangle(PdfName.Rect)),fieldName,
field.getValueAsString(),defaultFont,defaultFontSize);
form.replaceField(字段名,newField);
}
form.flattfields();

虽然字体信息不是动态设置的,但它还是可以工作的。

所以最后我打开了PDF。看起来Acrobat Reader确实设置了子类型,但它是
/Widget
。我找到的最后一个解决方法是手动将子类型设置为
/XObject
,然后调用
form.flatte()
。这还会保留字段上的字体信息

请注意,手动更改子类型可能会破坏其他功能。我不是一个PDF专家,所以我不能确定。然而,现在我只是改变了子类型并立即将其展平,所以这应该不是一个大问题

此代码的最终版本如下所示:

for(String fieldName : keys) {
    PdfFormField field = fields.get(fieldName);
    PdfDictionary fieldObject = field.getPdfObject();

    if(fieldObject.get(PdfName.Subtype) != PdfName.XObject) {
        fieldObject.put(PdfName.Subtype, PdfName.XObject);
    }
}

form.flattenFields();
pdf.close();

通过这种方式,它看起来就像预期的那样工作。

因此在最后,我打开了PDF。看起来Acrobat Reader确实设置了子类型,但它是
/Widget
。我找到的最后一个解决方法是手动将子类型设置为
/XObject
,然后调用
form.flatte()
。这还会保留字段上的字体信息

请注意,手动更改子类型可能会破坏其他功能。我不是一个PDF专家,所以我不能确定。然而,现在我只是改变了子类型并立即将其展平,所以这应该不是一个大问题

此代码的最终版本如下所示:

for(String fieldName : keys) {
    PdfFormField field = fields.get(fieldName);
    PdfDictionary fieldObject = field.getPdfObject();

    if(fieldObject.get(PdfName.Subtype) != PdfName.XObject) {
        fieldObject.put(PdfName.Subtype, PdfName.XObject);
    }
}

form.flattenFields();
pdf.close();

这样看来,它的工作原理与预期一致。

我遇到了完全相同的问题。似乎Adobe Reader也是出于某种原因的原因。我找到的另一个解决方案似乎是将字段设置为只读(iText7):

使用(iText.Kernel.Pdf.PdfDocument pdfStamper=new iText.Kernel.Pdf.PdfDocument(pdfReader,pdfWriter))
{
iText.Forms.PdfAcroForm pdfForm=iText.Forms.PdfAcroForm.GetAcroForm(pdfStamper,true);
if(pdfForm!=null)
{
System.Collections.Generic.IDictionary FormFields=pdfForm.GetFormFields();
foreach(FormFields中的KeyValuePair kv)
{
FormFields[kv.Key].SetReadOnly(true);
}
}
WProps.SetFullCompressionMode(假);
pdfStamper.GetWriter().SetCloseStream(false);
}

我遇到了完全相同的问题。似乎Adobe Reader也是出于某种原因的原因。我找到的另一个解决方案似乎是将字段设置为只读(iText7):

使用(iText.Kernel.Pdf.PdfDocument pdfStamper=new iText.Kernel.Pdf.PdfDocument(pdfReader,pdfWriter))
{
iText.Forms.PdfAcroForm pdfForm=iText.Forms.PdfAcroForm.GetAcroForm(pdfStamper,true);
if(pdfForm!=null)
{
System.Collections.Generic.IDictionary FormFields=pdfForm.GetFormFields();
foreach(FormFields中的KeyValuePair kv)