iTextSharp RenameField错误?

时间:2014-01-25 00:07:08

标签: itextsharp itext

我正在尝试重命名子类的复选框。假设复选框的名称是MyForm.Check1.page0。我跑的时候:

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

该复选框仅重命名为“newName”。子类信息被删除。我从文档中得到了子类无法更改的信息,但这是出乎意料的。

根据文件:

  

“重命名字段。只能重命名名称的最后部分   例如,如果原始字段是“ab.cd.ef”,则只有“ef”部分可以   更名“。

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

GitHub上的

I found the source class对我来说它看起来像个错误。来自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#项目中,它应该可以工作。您将需要一个带有文本字段的PDF,其名称设置为“TableStartPosition”,或者在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);
        }
    }
}

2 个答案:

答案 0 :(得分:1)

也许作者没有完全解释它,但我认为确实存在一个错误。 我们假设我们有一个带有AcroForm的PDF文档,其中包含两个字段:nameowner.name。现在,我们要将owner.name重命名为owner.name1,将name重命名为name1。第一个将成功,但现场注册表(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);

答案 1 :(得分:0)

没有子类的字段。字段名称中的点指的是层次结构。例如:如果您有一个名为person的字段。此字段可以包含nameaddress等子项。这些子字段的完全限定名称将为person.nameperson.address。地址又可以包含子字段,例如streetcitycountry,从而生成完全限定的名称,例如person.address.streetperson.address.city和{{1 }}

您可以重命名字段,例如person.address.country,但不能更改层次结构,因为PDF中的任何位置都不存在完全限定名称。因此,如果您的字段具有完全限定的名称street,则只能重命名person.address.street部分。例如:您可以将street重命名为person.address.street。您无法将person.address.line1重命名为person.address.street,因为这会更改表单的结构。

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

你说某事感觉像是一个错误,但我没有看到什么是错的。文档表明person.street是可以更改的完全限定名称的唯一部分。没有任何地方说你可以将ef更改为ab.cd.ef,因为这意味着你必须重写一个由不同PDF字典组成的结构树,而不是在一个字典中重命名一个键的值。 / p>

顺便说一句,这也在my book中解释过。

<强>附录:

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