2014-01-25 49 views
4

我正在尝试重命名子类型的复选框。假设复选框的名称是MyForm.Check1.page0。当我运行时:iTextSharp RenameField错误?

reader.AcroField.RenameField("MyForm.Check1.page0", "MyForm.Check1.newName"); 

该复选框被重命名为“newName”。子类信息被删除。我从文档中得到子类不能改变,但这是意外的。

根据文档:“只有‘

’。重命名字段只有名字的最后一部分可以被重命名为 例如,如果原始字段为” ab.cd.ef如果“部分可以更名为 ”。

这是一个漫长的一天,但我会阅读意味着你可以用“ab.cd.ef”到“ab.cd.yz”的子类,重命名字段,而不是如果重命名“ ab.cd.ef“改为”ab.cd.yz“,您最终会得到一个名为”yz“的字段。

I found the source class在GitHub上,它看起来像它对我来说是一个错误。从GitHub:

public bool RenameField(String oldName, String newName) { 
      int idx1 = oldName.LastIndexOf('.') + 1; 
      int idx2 = newName.LastIndexOf('.') + 1; 
      <snip> 
      Item item = fields[oldName]; 
      newName = newName.Substring(idx2); 

我认为问题是最后一行。如果这是设计,我觉得很奇怪。我想我可以解决它,但它又似乎很奇怪。

编辑:

我抄和清理的代码,并提出了sample command line tool显示这个问题。您只需下载副本并更改PDF的路径即可。这一切都来自一个更大的应用程序,所以它作为一个测试应用程序有点臃肿,但它比试图重写所有内容要快。其中一些代码在工作过程中稍显松动。我也遗漏了一些与这个问题无关的代码(即我插入的JavaScript代码)。

如果您希望DropBox以外的传递机制让我知道。

我也在粘贴下面的.cs文件的细节。你应该能够将它粘贴到C#项目中,并且它应该可以工作。您将需要一个带有名称设置为“TableStartPosition”的文本字段的PDF,或者调整FillCoverPage方法中的fieldPositions对象并更新硬编码路径。

当您运行该应用程序时,会添加一些复选框,指定名称为“InkSaver.chk2.pageX”(其中X是数字)。然而,在运行结束时,复选框只被命名为“pageX”。您可以在RenameFields()方法中观看代码。

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using iTextSharp.text; 
using iTextSharp.text.pdf; 

namespace RenameFieldTest 
{ 
    class Program 
    { 
     Stream _pdfTemplateStream; 
     MemoryStream _pdfResultStream; 

     PdfReader _pdfTemplateReader; 
     PdfStamper _pdfResultStamper; 

     static void Main(string[] args) 
     { 
      Program p = new Program(); 
      try 
      { 
       p.RunTest(); 
      } 
      catch (Exception f) 
      { 
       Console.WriteLine(f.Message); 
       Console.ReadLine(); 
      } 
     } 
     internal void RunTest() 
     { 
      FileStream fs = File.OpenRead(@"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\CoverPage.pdf"); 
      _pdfTemplateStream = fs; 
      _pdfResultStream = new MemoryStream(); 
      //PDFTemplateStream = new FileStream(_templatePath, FileMode.Open); 
      _pdfTemplateReader = new PdfReader(_pdfTemplateStream); 
      _pdfResultStamper = new PdfStamper(_pdfTemplateReader, _pdfResultStream); 

      #region setup objects 
      List<CustomCategory> Categories = new List<CustomCategory>(); 
      CustomCategory c1 = new CustomCategory(); 
      c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]); 
      c1.CategorySizesInUse.Add(CustomCategory.AvailableSizes[2]); 
      Categories.Add(c1); 

      CustomCategory c2 = new CustomCategory(); 
      c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[0]); 
      c2.CategorySizesInUse.Add(CustomCategory.AvailableSizes[1]); 
      Categories.Add(c2); 

      List<CustomObject> Items = new List<CustomObject>(); 
      CustomObject co1 = new CustomObject(); 
      co1.Category = c1; 
      co1.Title = "Object 1"; 
      Items.Add(co1); 

      CustomObject co2 = new CustomObject(); 
      co2.Category = c2; 
      co2.Title = "Object 2"; 
      Items.Add(co2); 

      #endregion 

      FillCoverPage(Items); 
      _pdfResultStamper.Close(); 
      _pdfTemplateReader.Close(); 

      List<MemoryStream> pdfStreams = new List<MemoryStream>(); 
      pdfStreams.Add(new MemoryStream(_pdfResultStream.ToArray())); 

      MergePdfs(@"C:\temp\a\RenameFieldTest\RenameFieldTest\Library\Outfile.pdf", pdfStreams); 

      _pdfResultStream.Dispose(); 
      _pdfTemplateStream.Dispose(); 
     } 
     internal void FillCoverPage(List<CustomObject> Items) 
     { 

      //Before we start we need to figure out where to start adding the table 
      var fieldPositions = _pdfResultStamper.AcroFields.GetFieldPositions("TableStartPosition"); 
      if (fieldPositions == null) 
      { throw new Exception("Could not find the TableStartPosition field. Unable to determine point of origin for the table!"); } 

      _pdfResultStamper.AcroFields.RemoveField("TableStartPosition"); 

      var fieldPosition = fieldPositions[0]; 
      // Get the position of the field 
      var targetPosition = fieldPosition.position; 

      //First, get all the available card sizes 
      List<string> availableSizes = CustomCategory.AvailableSizes; 


      //Generate a table with the number of available card sizes + 1 for the device name 
      PdfPTable table = new PdfPTable(availableSizes.Count + 1); 
      float[] columnWidth = new float[availableSizes.Count + 1]; 
      for (int y = 0; y < columnWidth.Length; y++) 
      { 
       if (y == 0) 
       { columnWidth[y] = 320; } 
       else 
       { columnWidth[y] = 120; } 
      } 

      table.SetTotalWidth(columnWidth); 
      table.WidthPercentage = 100; 

      PdfContentByte canvas; 

      List<PdfFormField> checkboxes = new List<PdfFormField>(); 

      //Build the header row 
      table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes))); 

      //Insert the global check boxes 
      PdfPCell[] globalRow = new PdfPCell[availableSizes.Count + 1]; 
      Phrase tPhrase = new Phrase("Select/Unselect All"); 
      PdfPCell tCell = new PdfPCell(); 
      tCell.BackgroundColor = BaseColor.LIGHT_GRAY; 
      tCell.AddElement(tPhrase); 
      globalRow[0] = tCell; 

      for (int x = 0; x < availableSizes.Count; x++) 
      { 
       tCell = new PdfPCell(); 
       tCell.BackgroundColor = BaseColor.LIGHT_GRAY; 
       PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer); 
       string fieldName = string.Format("InkSaver.Global.chk{0}", availableSizes[x].Replace(".", "")); 
       //f.FieldName = fieldName; 
       string js = string.Format("hideAll(event.target, '{0}');", availableSizes[x].Replace(".", "")); 
       f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer); 
       tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName); 
       globalRow[x + 1] = tCell; 
       checkboxes.Add(f); 
      } 
      table.Rows.Add(new PdfPRow(globalRow)); 

      int status = 0; 
      int pageNum = 1; 

      for (int itemIndex = 0; itemIndex < Items.Count; itemIndex++) 
      { 
       tCell = new PdfPCell(); 
       Phrase p = new Phrase(Items[itemIndex].Title); 
       tCell.AddElement(p); 
       tCell.HorizontalAlignment = Element.ALIGN_LEFT; 

       PdfPCell[] cells = new PdfPCell[availableSizes.Count + 1]; 
       cells[0] = tCell; 

       for (int availCardSizeIndex = 0; availCardSizeIndex < availableSizes.Count; availCardSizeIndex++) 
       { 
        if (Items[itemIndex].Category.CategorySizesInUse.Contains(availableSizes[availCardSizeIndex])) 
        { 
         string str = availableSizes[availCardSizeIndex]; 
         tCell = new PdfPCell(); 
         tCell.PaddingLeft = 10f; 
         tCell.PaddingRight = 10f; 
         cells[availCardSizeIndex + 1] = tCell; 
         cells[availCardSizeIndex].HorizontalAlignment = Element.ALIGN_CENTER; 

         PdfFormField f = PdfFormField.CreateCheckBox(_pdfResultStamper.Writer); 
         string fieldName = string.Format("InkSaver.chk{0}.{1}", availableSizes[availCardSizeIndex].Replace(".", ""), itemIndex + 1); 
         //f.FieldName = fieldName; <-- This causes the checkbox to be double-named (i.e. InkSaver.Global.chk0.InkSaver.Global.chk0 
         string js = string.Format("hideCardSize(event.target, {0}, '{1}');", itemIndex + 1, availableSizes[availCardSizeIndex]); 
         f.Action = PdfAction.JavaScript(js, _pdfResultStamper.Writer); 
         tCell.CellEvent = new ChildFieldEvent(_pdfResultStamper.Writer, f, fieldName); 

         checkboxes.Add(f); 
        } 
        else 
        { 
         //Add a blank cell 
         tCell = new PdfPCell(); 
         cells[availCardSizeIndex + 1] = tCell; 
        } 
       } 
       //Test if the column text will fit 

       table.Rows.Add(new PdfPRow(cells)); 

       canvas = _pdfResultStamper.GetUnderContent(pageNum); 
       ColumnText ct2 = new ColumnText(canvas); 
       ct2.AddElement(new PdfPTable(table)); 
       ct2.Alignment = Element.ALIGN_LEFT; 
       ct2.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0); 
       status = ct2.Go(true); 

       if ((status != ColumnText.NO_MORE_TEXT) || (itemIndex == (Items.Count - 1))) 
       { 
        ColumnText ct3 = new ColumnText(canvas); 
        ct3.AddElement(table); 
        ct3.Alignment = Element.ALIGN_LEFT; 
        ct3.SetSimpleColumn(targetPosition.Left, 0, targetPosition.Right, targetPosition.Top, 0, 0); 
        ct3.Go(); 

        foreach (PdfFormField f in checkboxes) 
        { 
         _pdfResultStamper.AddAnnotation(f, pageNum); 
        } 
        checkboxes.Clear(); 

        if (itemIndex < (Items.Count - 1)) 
        { 
         pageNum++; 
         _pdfResultStamper.InsertPage(pageNum, _pdfTemplateReader.GetPageSize(1)); 

         table = new PdfPTable(availableSizes.Count + 1); 
         table.SetTotalWidth(columnWidth); 
         table.WidthPercentage = 100; 
         table.Rows.Add(new PdfPRow(this.GetTableHeaderRow(availableSizes))); 
        } 
       } 
      } 
     } 
     private PdfPCell[] GetTableHeaderRow(List<string> AvailableSizes) 
     { 
      PdfPCell[] sizeHeaders = new PdfPCell[AvailableSizes.Count + 1]; 
      Phrase devName = new Phrase("Device Name"); 
      PdfPCell deviceHeader = new PdfPCell(devName); 
      deviceHeader.HorizontalAlignment = Element.ALIGN_CENTER; 
      deviceHeader.BackgroundColor = BaseColor.GRAY; 
      sizeHeaders[0] = deviceHeader; 
      for (int x = 0; x < AvailableSizes.Count; x++) 
      { 
       PdfPCell hCell = new PdfPCell(new Phrase(AvailableSizes[x])); 
       hCell.HorizontalAlignment = Element.ALIGN_CENTER; 
       hCell.BackgroundColor = BaseColor.GRAY; 
       sizeHeaders[x + 1] = hCell; 
      } 
      return sizeHeaders; 
     } 
     public void MergePdfs(string filePath, List<MemoryStream> pdfStreams) 
     { 
      //Create output stream    
      FileStream outStream = new FileStream(filePath, FileMode.Create); 

      if (pdfStreams.Count > 0) 
      { 
       try 
       { 
        int PriceCardCounter = 0; 
        //Create Main reader 
        PdfReader reader = new PdfReader(pdfStreams[0]); 

        //rename fields in the PDF. This is required because PDF's cannot have more than one field with the same name 
        RenameFields(reader, PriceCardCounter++); 

        //Create main writer 
        PdfCopyFields Writer = new PdfCopyFields(outStream); 

        //Open document for writing 
        ////Add pages 
        Writer.AddDocument(reader); 

        //For each additional pdf after first combine them into main document 
        foreach (var PdfStream in pdfStreams.Skip(1)) 
        { 
         PdfReader reader2 = new PdfReader(PdfStream); 
         //rename PDF fields 
         RenameFields(reader2, PriceCardCounter++); 
         // Add content 
         Writer.AddDocument(reader2); 
        } 

        Writer.Close(); 
       } 
       finally 
       { 
        foreach (var Strm in pdfStreams) 
        { 
         try { if (null != Strm) Strm.Dispose(); } 
         catch { } 
        } 
        outStream.Close(); 
       } 
      } 
     } 
     private void RenameFields(PdfReader reader, int PriceCardID) 
     { 
      int tempPageNum = 1; 
      //rename all fields 
      foreach (string field in reader.AcroFields.Fields.Keys) 
      { 
       if (((reader.AcroFields.GetFieldType(field) == 1) || (reader.AcroFields.GetFieldType(field) == 2)) && (field.StartsWith("InkSaver"))) 
       { 
        //This is a InkSaver button, set the name so its subclassed 
        string classPath; 
        if (reader.AcroFields.GetFieldType(field) == 2) 
        { 
         classPath = field.Substring(0, field.LastIndexOf(".")); 
         if (field.StartsWith("InkSaver.chk")) 
         { 
          int a = field.LastIndexOf("."); 
          string sub = field.Substring(a + 1, (field.Length - a - 1)); 
          int pageNum = int.Parse(sub); 
          int realPageNum = pageNum + tempPageNum;//PostProcessing.Instance.CoverPageLength; 
          PriceCardID = realPageNum; 
         } 
        } 
        else 
        { 
         classPath = field.Substring(0, field.LastIndexOf(".")); 
        } 
        string newID = classPath + ".page" + PriceCardID.ToString(); 
        bool ret = reader.AcroFields.RenameField(field, newID); 
       } 
       else 
       { 
        reader.AcroFields.RenameField(field, field + "_" + PriceCardID.ToString());// field + Guid.NewGuid().ToString("N")); 
       } 
      } 
     } 
    } 
    public class ChildFieldEvent : IPdfPCellEvent 
    { 
     protected PdfWriter writer; 
     protected PdfFormField parent; 
     protected string checkBoxName; 

     internal ChildFieldEvent(PdfWriter writer, PdfFormField parent, string CheckBoxName) 
     { 
      this.writer = writer; 
      this.parent = parent; 
      this.checkBoxName = CheckBoxName; 
     } 

     public void CellLayout(PdfPCell cell, Rectangle rect, PdfContentByte[] cb) 
     { 
      createCheckboxField(rect); 
     } 
     private void createCheckboxField(Rectangle rect) 
     { 
      RadioCheckField bt = new RadioCheckField(this.writer, rect, this.checkBoxName, "Yes"); 
      bt.CheckType = RadioCheckField.TYPE_SQUARE; 
      bt.Checked = true; 
      this.parent.AddKid(bt.CheckField); 
     } 
    } 
} 

回答

0

有没有这样的事情作为一个场与子类。字段名称中的点是指层次结构。例如:如果您有一个名为person的字段。该字段可以包含子项,例如nameaddress。这些子字段的完全限定名称将是person.nameperson.address。地址可以具有子字段,如street,citycountry,从而生成完全限定名称,如person.address.street,person.address.cityperson.address.country

您可以重命名字段,如street,但不能更改层次结构,因为完全限定的名称不在PDF中的任何位置。所以如果你有一个完全合格的名字person.address.street的字段,你只能重命名street部分。例如:您可以将person.address.street重命名为person.address.line1。您不能将person.address.street重命名为person.street,因为这会更改表格的结构。

文档说的是您可以重命名字段名称,但不能更改字段结构。

你说有东西感觉是一个错误,但我不明白什么是错的。该文档指出ef是可以更改的完全限定名称的唯一部分。没有其他人说你可以将ab.cd.ef更改为xy,因为这意味着你必须重写由不同的PDF字典组成的结构树,而不是重命名单个字典中的密钥值。

顺便说一下,这也是在my book中解释的。

附录:

我创建了一个名为RenameField一个简单的例子。它采用表格(subscribe.pdf)并创建一个新表格(subscribe_renamed.pdf)。区别?我们已将"personal.loginname"更名为"personal.login"。将"personal.loginname"重命名为"login"是不可能的,因为这需要更改层次结构(只需对其进行测试,您会发现它不起作用)。

+0

我使用了错误的术语道歉。我似乎记得Adobe文献中的某个地方,命名层次被称为子类。但是,您的解释与我的理解一致,并强调这是一个错误。要使用您的示例,请使用iTextSharp,如果我尝试将person.address.street的FieldName重命名为person.address.line1,则该字段将重命名为line1,并删除层次结构。如果仔细查看newName = newName.Substring(idx2),可以看到应用于新字段的唯一值是最后一个“。”后面的谓词。 –

+0

我在其他回复中用完了字符。如果新字段应包含前一字段的层次结构,那么代码应该读取像newName = newName.Substring(0,idx2)+ newName;或者因为您在区域中进行了验证,oldName和newName的层次匹配,所以您不应该在做Substring,只是将newName应用于该字段。 –

+0

给我证明你的观点的SSCCE。 –

1

也许作者没有完全解释它,但我认为真的有一个错误。 我们假设我们有一个带有AcroForm的PDF文档,其中包含两个字段:nameowner.name。现在我们要将owner.name重命名为owner.name1namename1。第一个将成功,但在字段注册表(AcroFields.fields)中的名称现在是name1,而不是owner.name1(如此之前它是owner.name)。秒重命名失败,因为name1已存在于字段注册表中。 它只影响注册表,生成的PDF文档中的字段名称是正确的。

这里从Java iText的关键代码片段:

// snippet from com.itextpdf.text.pdf.AcroFields.renameField 
int idx2 = newName.lastIndexOf('.') + 1; 
// cut the last part from the original name 
newName = newName.substring(idx2); 
PdfString ss = new PdfString(newName, PdfObject.TEXT_UNICODE); 
// problem: only the last part will be registered, this must 
// be IMO the (original) whole name including the dots 
fields.put(newName, item);