复制iTextSharp 5.4.5.0中的字段

时间:2014-02-11 21:35:03

标签: itextsharp itext

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

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

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

(对不起,很长一例,它是从一个更大的应用程序中挑选出来的。你需要一个带有名为“TableStartPosition”的字段的PDF,并使用正确的路径更新RunTest以获得本地机器工作。)

PdfCopy功能还没有进入iTextSharp吗?我使用的是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; }
}

1 个答案:

答案 0 :(得分:0)

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

copy.setMergeFields();

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