首页 文章

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

提问于
浏览
1

我正在使用 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 文档按预期工作。我可以在 Adobe Acrobat Reader 中打开它,填写可编辑字段,保存,重新打开等。

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

我这个代码执行此操作:

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)压缩编辑(和保存)的字段时才会出现此问题。如果我以编程方式编辑字段,e.g. 使用 iText 的PdfFormField#setValue(String)方法,展平的 PDF 渲染值很好。

似乎 Adobe Reader 正在设置表单字段的某些属性,以防止它们被正确展平......实际情况是这样吗?它有什么办法吗?
谢谢

----------------EDIT----------------------

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

由 iText7 生成:<</BBox [0 0 523 109 ] /Filter /FlateDecode /Length 95 /Matrix [1 0 0 1 0 0 ] /Resources <</Font <</F1 23 0 R >> >> /Subtype /Form /Type /XObject >>

由 Acrobat Reader 保存:<</BBox [0.0 0.0 523.0 109.0 ] /Filter /FlateDecode /Length 107 /Resources <</Font <</F1 21 0 R >> >> >>

如您所见,缺少/Subtype部分。每当您尝试禁用表单字段时,e.g. PdfAcroForm#flattenFields()PdfFormField#setReadOnly(boolean),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();

即使字体信息没有动态设置,它也有效,但这是另一个故事

2 回答

  • 0

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

    请注意,手动更改子类型可能会破坏其他内容。我不是一个 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();
    

    这样它看起来像预期的那样工作。

  • 0

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

    using (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<String, iText.Forms.Fields.PdfFormField> FormFields = pdfForm.GetFormFields();
    
                            foreach (KeyValuePair<String, iText.Forms.Fields.PdfFormField> kv in FormFields)
                            {
                                FormFields[kv.Key].SetReadOnly(true);
                            }
                        }
    
                        WProps.SetFullCompressionMode(false);
                        pdfStamper.GetWriter().SetCloseStream(false);
                    }
    

相关问题