【问题标题】:Is it possible to modify PDF Form Field Names?是否可以修改 PDF 表单字段名称?
【发布时间】:2011-01-21 13:37:39
【问题描述】:

情况是这样的。我有一个带有自动生成的 pdf 表单字段名称的 PDF。问题是这些名称对用户不是很友好。它们看起来像: topmostSubform[0].Page1[0].Website_Address[0]

我希望能够更改它们,使它们类似于网站地址。我可以访问 ABCPDF 并且有使用 iTextSharp 的经验,但我尝试使用这些 API 来执行此操作(访问表单字段并尝试重命名),但似乎不可能。

有没有人有任何尝试通过某种 API(最好是开源的)来做到这一点的经验。代码也是.Net。

【问题讨论】:

    标签: .net pdf itextsharp abcpdf


    【解决方案1】:

    好消息:您可以在 iTextSharp 中更改字段名称。

    但您实际上无法编辑 PDF。您将阅读现有的 PDF,更新内存中的字段名称,然后写出修改后的 PDF。要更改字段名称,请调用 AcroFields.RenameField 方法。

    这是一个sn-p:

    PdfReader reader = new PdfReader(PDF_PATH);
    using (FileStream fs = new FileStream("Test Out.pdf", FileMode.Create)) {
        PdfStamper stamper = new PdfStamper(reader, fs);
        AcroFields fields = stamper.AcroFields;
        fields.RenameField("oldFieldName", "newFieldName");
        stamper.Close();
    }
    

    现在有个坏消息:您可以在重命名的字段中使用的字符似乎存在限制。

    我用您的示例字段名称测试了上面的 sn-p,但它不起作用。删除句点,它确实有效。我不确定是否有解决方法,但这对您来说可能是个问题,

    【讨论】:

      【解决方案2】:

      AcroForm 字段的全名未明确存储在字段中。它实际上是从字段层次结构派生的,左侧出现了一个以点分隔的祖先列表。

      因此,仅将字段从“topmostSubform[0].Page1[0].Website_Address[0]”重命名为“WebsiteAddress”不太可能产生正确的结果。

      您会发现 PDF 参考的第 8.6.2 节“字段字典”很好地解释了字段命名的工作原理;-)

      基本上,AcroForm 中的每个字段都由字典定义,其中可能包含与字段名称相关的某些可选条目。

      • 键“/T”指定部分名称。在您的问题中,'topmostSubform[0]'、'Page1[0]' 和 Website_Address[0] 都代表部分名称。

      • Key '/TU' 为字段指定了一个替代的“用户友好”名称,可用于代替实际字段名称来识别用户界面中的字段。

      与其重命名相关字段,不如考虑添加 /TU 条目!

      下面的示例使用 ABCpdf 遍历 AcroForm 中的所有字段,并根据其部分名称将备用名称插入到字段中。

      VBScript:

      Set theDoc = CreateObject("ABCpdf7.Doc")
      theDoc.Read "myForm.pdf"
      
      Dim theFieldIDs, theList
      theFieldIDs = theDoc.GetInfo(theDoc.Root, "Field IDs")
      theList = Split(theFieldIDs, ",")
      
      For Each fieldID In theList
          thePartialName = theDoc.GetInfo(fieldID, "/T:text")
          theDoc.SetInfo fieldID, "/TU:text", thePartialName
      Next
      
      theDoc.Save "output.pdf"
      theDoc.Clear
      

      "/TU:text" 更改为"/T:text" 将设置字段的部分名称。

      可以在此处找到用 C# 和 VB.NET 编写的函数示例: Doc.GetInfoDoc.SetInfo。另请参阅Object Paths 上的文档。

      【讨论】:

        【解决方案3】:

        我昨天遇到了这个问题,在论坛和其他人尝试了答案后,但没有取得任何进展。我的代码看起来像这样。

        // Open up the file and read the fields on it.
        var pdfReader = new PdfReader(PATH_TO_PDF);
        var fs = new FileStream(pdfFilename, FileMode.Create, FileAccess.ReadWrite)
        var stamper = new PdfStamper(pdfReader, fs);
        var pdfFields = stamper.AcroFields;
        
        //I thought this next line of code I commented out will do it
        //pdfFields.RenameField("currentFieldName", "newFieldName");
        
        // It did for some fields, but returned false for others.
        // Then I looked at the AcroFields.RenameField method in itextSharp source and noticed some restrictions. You may want to do the same.
        //  So I replaced that line  pdfFields.RenameField(currentFieldName, newFieldName); with these 5 lines
        
        AcroFields.Item item = pdfFields.Fields[currentFieldName];
        PdfString ss = new PdfString(newFieldName, PdfObject.TEXT_UNICODE);
        item.WriteToAll(PdfName.T, ss, AcroFields.Item.WRITE_VALUE | AcroFields.Item.WRITE_MERGED);
        item.MarkUsed(pdfFields, AcroFields.Item.WRITE_VALUE);
        pdfFields.Fields[newFieldName] = item;
        

        这样就完成了

        【讨论】:

          【解决方案4】:

          也许你可以考虑一下:

          • iText 不是免费的,在 专有 软件环境中(2.1.7 it/s 最后一个“免费”版本)。
          • AbcPDF 也不是免费的。

          我不得不说我们有 AbcPDF 许可证,并且我们在 Java 项目中也使用 iText 2.1.7...所以我可以说我同意以前的答案,但是,你不能使用/购买这个产品,您可以尝试将名称替换为纯 pdf 代码(作为纯 txt 文件),遵循 PDF 规范:

          签名字段示例:

          35 0 obj
          <<
          /AP <<
          /N 37 0 R
          >>
          /DA (/TimesRoman 0 Tf 0 g)
          /F 4
          /FT /Sig
          /P 29 0 R
          /Rect [ 86 426 266 501 ]
          /Subtype /Widget
          /T (FIELD_MODIF)
          /Type /Annot
          /V 36 0 R
          >>
          endobj
          

          “FIELD_MODIF”是放置新名称的地方。

          【讨论】:

          • 这工作了一次,但后来我添加了复选框、单选按钮和一个下拉列表,看看它是否适用于这些。保存 pdf 并在 notepad++ 中查看原始文本后,我无法再识别原始文本中的任何字段名称。查找不会找到任何东西。不过这是个好主意……这个问题已经困扰了我好几年了。
          【解决方案5】:

          虽然您不能使用 javascript 重命名字段,但您可以添加新字段并删除现有字段。您还可以在文档之间剪切和粘贴。所以...

          一个。开发重命名脚本,例如:

              var doc = app.activeDocs[0];
              var fnames = new Array();
              for ( var i = 0; i < doc.numFields - 1; i++) {      
                  fnames[i] = doc.getNthFieldName(i);
              }
              for (var i = 0; i < doc.numFields - 1; i++){        
                  var f = doc.getField(fnames[i] + ".0");
                  var nfn = fnames[i].replace("1","2");
                  var rb = doc.addField(nfn,"radiobutton",0,f.rect)
                  for ( var j = 1; j < 9; j++){//Add the other 8
                      f = doc.getField(fnames[i] + "." + j);
                      doc.addField(nfn,"radiobutton",0,f.rect)
                  }
                  rb.setExportValues([1,2,3,4,5,6,7,8,9]);
                  rb.borderStyle = f.borderStyle;
                  rb.strokeColor = f.strokeColor;
                  rb.fillColor = f.fillColor;
                  doc.removeField(fnames[i]);
                  console.println(fnames[i] + " to " + nfn);
              }
          

          注意事项:
          重命名字段可能会更改 getNthFieldName 的字段顺序,因此请先获取这些字段。
          一组单选按钮是一个字段 (getField("Groupname")),要获取同一组中的第 n 个使用 getField("Groupname.n"),您需要这些用于位置矩形。适用于所有人的属性可以设置为 enmase。
          addField 参数有:字段名、字段类型、页面、位置矩形)
          在示例中,每组中有 9 个单选按钮

          两个。将要重命名的字段剪切并粘贴为空白 pdf(我假设上面有一页)。运行脚本。剪切并粘贴回来。

          如果在运行脚本时保持两个文档都打开,您可能需要将 app.activeDocs[0] 更改为 app.activeDocs[1] 或将“doc”替换为“this”

          【讨论】:

            【解决方案6】:

            最好的方法是存储字段 rect,然后创建具有所需名称和存储 rect 位置的新字段,新字段将与旧字段位于同一位置,ta da :) 这是代码:

            Sub CreateNewField()
             Create = "var f = this.getField('" & oldFieldName & "'); var rect = f.rect; 
             this.addField('" & newFieldName & "','text',0,rect);"
             FormFields.ExecuteThisJavascript Create
             DeleteField 'sub to delete old field
            End Sub
            

            【讨论】:

            • OP 的问题是关于通过 API 编辑字段名称。即使您的答案是有效的,也最好发布一个代码示例。
            • 我为迟到的回复道歉。我也找了半天想改字段名,最后才发现最好把要重命名的字段的位置记录下来,然后擦掉,换个想要的名字的新字段在同一个地方。我通过使用 java 脚本但通过 excel vba 实现了这一点。
            【解决方案7】:

            是的,可以重命名表单字段。我没有任何可以帮助您解决此问题的源代码 API 的经验,但我的公司 PDF SDK can help 您这样做了,并且通过一点搜索发现 iText will indeed let you rename form fields

            【讨论】:

              【解决方案8】:

              这是在“iText in Action”一书中找到的一个java示例

              这摘自他们为本书提供的示例源代码,在同样的问题上对我有很大帮助。 part2.chapter06.ConcatenateForms2

              public static void main(String[] args)
                  throws IOException, DocumentException {
                  // Create a PdfCopyFields object
                  PdfCopyFields copy
                      = new PdfCopyFields(new FileOutputStream(RESULT));
                  // add a document
                  PdfReader reader1 = new PdfReader(renameFieldsIn(DATASHEET, 1));
                  copy.addDocument(reader1);
                  // add a document
                  PdfReader reader2 = new PdfReader(renameFieldsIn(DATASHEET, 2));
                  copy.addDocument(reader2);
                  // Close the PdfCopyFields object
                  copy.close();
                  reader1.close();
                  reader2.close();
              }
              
              
              private static byte[] renameFieldsIn(String datasheet, int i)
                  throws IOException, DocumentException {
                  ByteArrayOutputStream baos = new ByteArrayOutputStream();
                  // Create the stamper
                  PdfStamper stamper = new PdfStamper(new PdfReader(datasheet), baos);
                  // Get the fields
                  AcroFields form = stamper.getAcroFields();
                  // Loop over the fields
                  Set<String> keys = new HashSet<String>(form.getFields().keySet());
                  for (String key : keys) {
                      // rename the fields
                      form.renameField(key, String.format("%s_%d", key, i));
                  }
                  // close the stamper
                  stamper.close();
                  return baos.toByteArray();
              }
              

              【讨论】:

                【解决方案9】:

                这对我使用 iText 7 有效:

                PdfReader reader = new PdfReader("Source.pdf");
                
                using (FileStream fs = new FileStream("Dest.pdf", FileMode.Create))
                {
                    using (var pdfDoc = new PdfDocument(reader, new PdfWriter(fs)))
                    {
                        PdfAcroForm pdfForm = PdfAcroForm.GetAcroForm(pdfDoc, true);
                        pdfDoc.GetWriter().SetCloseStream(true);
                        
                        var Fields = pdfForm.GetFormFields().Select(x => x.Key).ToArray();
                        
                        for (int i = 0; i < Fields.Length; i++)
                        {
                            pdfForm.RenameField(Fields[i], "new_" + Fields[i]);
                        }                   
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 2016-08-28
                  • 1970-01-01
                  • 2011-01-08
                  • 2020-06-08
                  • 2020-11-25
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-01-12
                  • 2011-03-19
                  相关资源
                  最近更新 更多