【问题标题】:PDF AcroForm field modified first in Acrobat then in iText7 becomes blankPDF AcroForm 字段首先在 Acrobat 中修改,然后在 iText7 中变为空白
【发布时间】:2018-03-15 21:38:57
【问题描述】:

我正在使用 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 文档按预期工作。我可以在 Adob​​e 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)编辑(和保存)的字段时才会出现此问题。 如果我以编程方式编辑字段,例如使用 iText 的 PdfFormField#setValue(String) 方法,扁平化的 PDF 可以很好地呈现值。

似乎 Adob​​e Reader 正在设置表单字段的某些属性,以防止它们被正确展平......真的是这样吗?有什么办法可以解决吗?

【问题讨论】:

    标签: java pdf itext7 acrobat pdf-form


    【解决方案1】:

    显然这个问题是由 Adob​​e 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 部分缺失。每当您尝试禁用表单字段时,例如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();
    

    它有点工作,即使字体信息不是动态设置的,但这是another story

    编辑:

    所以最后我用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();
    

    这样看起来就像按预期工作一样。

    【讨论】:

      【解决方案2】:

      我遇到了同样的问题。出于某种原因,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);
                      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多