在iTextSharp 5.4.5.0中复制字段 [英] Copying fields in iTextSharp 5.4.5.0

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

问题描述

我的印象是现在可以使用PdfCopy复制AcroFields。在 iText 5.4.4.0的发行说明中,现在已尽可能列出。但是,当我尝试这样做时,它会显示所有注释(我认为我正确使用该术语,对于iText来说仍然相当新......)因为字段被删除了。看起来字段就在那里(意思是我可以看到指示可编辑字段的蓝色框),但它们不可编辑。如果我尝试在Acrobat中提交PDF,我会收到一条消息,说没有字段,你想让Acrobat发现它们吗?并且大多数都被找到并标记和字段正确(复选框不是,但是文本字段是)。

I was under the impression that it is now possible to copy AcroFields using PdfCopy. In the release notes for iText 5.4.4.0 this is listed as possible now. However, when I try to do so it appears all the annotations (I think I am using that term correctly, still fairly new to iText...) for the fields are stripped out. It looks like the fields are there (meaning I can see the blue boxes that indicate an editable field), but they are not editable. If I try to bring the PDF up in Acrobat I get a message saying that "there are no fields, would you like Acrobat to discover them?" and most are found and marked and fields properly (check boxes aren't, but the text fields are).

我假设在某些地方还有一个额外的步骤重新添加注释到PdfCopy对象,但我没有看到从PdfReader获取注释的方法。我似乎也找不到任何关于如何做到这一点的文档(因为AcfFields在PdfCopy中长期不受支持,因此我发现的大部分内容都是这样的)。

I assume there is an additional step somewhere along the lines to re-add the annotations to the PdfCopy object, but I do not see a way to get the annotations from the PdfReader. I also cannot seem to find any documentation on how to do this (since AcroFields were for so long not supported in PdfCopy most of what I find is along that vein).

由于敏感性,我无法提供相关PDF的副本,但使用之前使用的测试程序的更改版本,您可以看到以下代码的问题。它应该在右边的四列中生成一个带有一些复选框的表。如果我在MergePdfs方法中使用与PdfCopyFields完全相同的代码而不是PdfCopy,它会按预期工作。此代码不会生成任何文本字段,但在我的主项目中,它们是用作模板的原始父PDF的一部分。

Due to sensitivity I cannot provide a copy of the PDF's in question, but using an altered version of a test program used earlier you can see the issue with the following code. It should generate a table with some check boxes in the four right columns. If I use the exact same code with PdfCopyFields in the MergePdfs method instead of PdfCopy it works as expected. This code does not produce any text fields, but in my main project they are part of the original parent PDF that is used as a template.

(很抱歉,从一个更大的应用程序中挑选它。你需要一个带有名为TableStartPosition字段的PDF并更新RunTest使用本地计算机的正确路径使其工作。)

(Sorry for the long example, it has been cherry picked from a much larger application. You will need a PDF with a field named "TableStartPosition" somewhere in it and update RunTest with the correct paths for your local machine to get this to work.)

PdfCopy功能是否已进入iTextSharp?我使用的是版本5.4.5.0。

Has the PdfCopy functionality not made it into iTextSharp yet? I am using version 5.4.5.0.

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

        Document document = null;

        if (pdfStreams.Count > 0)
        {
            try
            {
                int PageCounter = 0;
                //Create Main reader
                PdfReader reader = new PdfReader(pdfStreams[0]);
                PageCounter = reader.NumberOfPages;//This is if we have multiple pages in the cover page, we need to adjust the offset.

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

                //Create Main Doc
                document = new Document(reader.GetPageSizeWithRotation(1));

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

                //Open document for writing
                document.Open();
                ////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, PageCounter++);
                    // Add content
                    Writer.AddDocument(reader);
                }

                //Writer.AddJavaScript(PostProcessing.GetSuperscriptJavaScript());
                Writer.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                if (document != null)
                    document.Close();

                foreach (var Strm in pdfStreams)
                {
                    try { if (null != Strm) Strm.Dispose(); }
                    catch { }
                }
                //pdfStamper.Close();
                outStream.Close();

            }
        }
    }
    private void RenameFields(PdfReader reader, int PageNum)
    {
        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;
                        PageNum = realPageNum;
                    }
                }
                else
                {
                    classPath = field.Substring(0, field.LastIndexOf("."));
                }
                string newID = classPath + ".page" + PageNum.ToString();
                bool ret = reader.AcroFields.RenameField(field, newID);
            }
            else
            {
                reader.AcroFields.RenameField(field, field + "_" + PageNum.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);
    }
}
internal class CustomCategory
{
    internal static List<string> AvailableSizes
    {
        get
        {
            List<string> retVal = new List<string>();
            retVal.Add("1");
            retVal.Add("2");
            retVal.Add("3");
            retVal.Add("4");

            return retVal;
        }
    }

    internal CustomCategory()
    {
        CategorySizesInUse = new List<string>();
    }
    internal List<string> CategorySizesInUse { get; set; }
}
internal class CustomObject
{
    internal string Title { get; set; }
    internal CustomCategory Category { get;set; }
}


推荐答案

请看一下 MergeForms 示例。你的例子对我来说太长了,但是乍一看,我错过了以下几行:

Please take a look at the MergeForms example. Your example is too long for me to read, but at first sight, I'm missing the following line:

copy.setMergeFields();

顺便说一句,在 MergeForms2 ,在合并表单之前也会重命名字段。

By the way, in MergeForms2, the fields are also renamed before the form is merged.

这篇关于在iTextSharp 5.4.5.0中复制字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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