iTextSharp RenameField错误? [英] iTextSharp RenameField bug?

查看:112
本文介绍了iTextSharp RenameField错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am attempting to rename a checkbox that is subclassed. Let's say the checkbox's name is MyForm.Check1.page0. When I run:

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

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

The checkbox is renamed just "newName". The subclass information is dropped. I get from the documentation that the subclass cannot be changed, but this was unexpected.

根据文档:

重命名一个字段。只能重命名名称的最后一部分。对于
例子,如果原始字段是ab.cd.ef,只有ef部分可以
重命名。

"Renames a field. Only the last part of the name can be renamed. For example, if the original field is "ab.cd.ef" only the "ef" part can be renamed."

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

It has been a long day but I would read that to mean you can rename a field with a subclass of "ab.cd.ef" to "ab.cd.yz", not that if you rename "ab.cd.ef" to "ab.cd.yz" you instead end up with a field named just "yz".

我在GitHub上找到了源类,看起来它对我来说是个错误。来自GitHub:

I found the source class on GitHub and it looks like its a bug to me. From 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);

我认为问题是最后一行。如果这是设计的话,对我来说似乎很奇怪。我想我可以解决它,但它再次看起来很奇怪。

I think the issue is that last line. If this is by design it seems very strange to me. I think I can work around it, but again it just seems very strange.

编辑:

我复制并清理了代码并制作了示例此命令行工具,显示此问题。您只需下载副本并更改PDF的路径即可。这一切都来自一个更大的应用程序,因此它作为一个测试应用程序有点臃肿,但它比尝试重写所有内容更快。有些代码有点草率,因为它正在进行中。我还遗漏了一些与此问题无关的代码(即我插入的JavaScript代码)。

I have copied and cleaned the code and made a sample command line tool that shows this issue. You should just have to download a copy and change the paths for the PDF's. This is all coming from a much larger application so its a bit bloated as a test application but it was faster than trying to rewrite everything. Some of the code is a bit sloppy as its a work in progress. I also left out some code that isn't relevant to this issue (namely the JavaScript code I am inserting).

如果您更喜欢DropBox以外的传递机制,请我知道。

If you would prefer a delivery mechanism other than DropBox let me know.

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

I am also pasting the details of the .cs file below. You should just be able to paste this into a C# project and it should work. You will need a PDF with a text field with the name set to "TableStartPosition" or else adjust the fieldPositions object in the FillCoverPage method and update the hard coded paths.

运行应用程序时,会添加一些复选框,这些复选框被分配名称InkSaver.chk2.pageX(其中X是数字)。但是在运行结束时,复选框只是命名为pageX。您可以在RenameFields()方法中查看代码。

When you run the application there will be some check boxes added that are being assigned a name of "InkSaver.chk2.pageX" (where X is a number). However at the end of the run the checkboxes are just named "pageX". You can watch the code in the RenameFields() method.

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);
        }
    }
}


推荐答案

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

Maybe the author didn't explain it completely, but I think there really is a bug. Let's assume we have a PDF document with an AcroForm which contains two fields: name and owner.name. Now we want to rename owner.name to owner.name1 and name to name1. The first one will succeed, but the name in the field registry (AcroFields.fields) is now name1, not owner.name1 as expected (before it was owner.name). The seconds rename then fails because name1 already exists in the field registry. It affects only the registry, the field name in the resulting PDF document is correct.

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

Here the critical code snippet from 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);

这篇关于iTextSharp RenameField错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆