C# iTextSharp-使用填充的带页脚的动态内容

C# iTextSharp-使用填充的带页脚的动态内容,c#,.net,itextsharp,footer,C#,.net,Itextsharp,Footer,我试图创建长度高度可变的文档,并使它们成为表驱动的文档。我正在尝试专门使用iTextSharp表来创建文档,包括页脚赘语。在下面的代码中,我试图将最后一个单元格填充到与剩余空间完全相等的大小,这样我的页脚将正好放在文档的底部,紧靠底部边距。当我尝试使用1、2或3英寸的文档边距时,这种方式很好,但当我使用5英寸的边距时,我的页脚开始换行。我认为这可能只是一个糟糕的数学问题。除了使用页面事件之外,有人有什么建议来纠正这个问题吗 *注意:在创建表格和测量段落高度时使用了两种辅助方法,除了单击按钮的主代

我试图创建长度高度可变的文档,并使它们成为表驱动的文档。我正在尝试专门使用iTextSharp表来创建文档,包括页脚赘语。在下面的代码中,我试图将最后一个单元格填充到与剩余空间完全相等的大小,这样我的页脚将正好放在文档的底部,紧靠底部边距。当我尝试使用1、2或3英寸的文档边距时,这种方式很好,但当我使用5英寸的边距时,我的页脚开始换行。我认为这可能只是一个糟糕的数学问题。除了使用页面事件之外,有人有什么建议来纠正这个问题吗

*注意:在创建表格和测量段落高度时使用了两种辅助方法,除了单击按钮的主代码外,还包括这两种方法

*注意:每个表都将SplitLate设置为false,以使内容在页面之间均匀流动;所有表格都有特定的宽度,并且宽度已锁定

        //declare path for the PDF file to be created
        string strpath = @"C:\CROauto\Junk\MSE.pdf";

        //instantiate a new PDF document
        //(regular portrait format, with half-inch margins all around)
        var doc = new Document(PageSize.LETTER,
                               iTextSharp.text.Utilities.InchesToPoints(1f),
                               iTextSharp.text.Utilities.InchesToPoints(1f),
                               iTextSharp.text.Utilities.InchesToPoints(5f),
                               iTextSharp.text.Utilities.InchesToPoints(.5f));

        //create a PDF table
        //(2 column, with no border)
        PdfPTable mas_tbl = clsPDF.CreateTable(2, false, cell_padding_bottom: 0f, total_width: doc.PageSize.Width - doc.LeftMargin - doc.RightMargin);

        //get base font types
        BaseFont font_tb = FontFactory.GetFont(FontFactory.TIMES_BOLD).BaseFont;
        BaseFont font_t = FontFactory.GetFont(FontFactory.TIMES).BaseFont;

        //create regular Font objects, with varying sizes
        iTextSharp.text.Font f10 = new iTextSharp.text.Font(font_t, 10);
        iTextSharp.text.Font f8 = new iTextSharp.text.Font(font_t, 8);

        //create regular Font objects, bold, with varying sizes
        iTextSharp.text.Font fb10 = new iTextSharp.text.Font(font_tb, 10);
        iTextSharp.text.Font fb8 = new iTextSharp.text.Font(font_tb, 8);

        //get image
        iTextSharp.text.Image da_img = iTextSharp.text.Image.GetInstance(@"C:\CROauto\Junk\img.gif");

        //scale the image to a good size
        da_img.ScaleAbsolute(iTextSharp.text.Utilities.InchesToPoints(.75f),
                             iTextSharp.text.Utilities.InchesToPoints(.75f));

        //create new PDF cell to hold image
        PdfPCell icell = new PdfPCell();

        icell.PaddingBottom = 0f;

        //make sure the "image" cell has no border
        icell.Border = iTextSharp.text.Rectangle.NO_BORDER;

        //add the image to the PDF cell
        icell.AddElement(da_img);

        //add the image cell to the table
        mas_tbl.AddCell(icell);

        //work with the return address
        PdfPCell ra = new PdfPCell();
        ra.Border = iTextSharp.text.Rectangle.NO_BORDER;
        ra.PaddingBottom = 5f;

        PdfPTable tblra = clsPDF.CreateTable(1, false);

        tblra.HorizontalAlignment = Element.ALIGN_RIGHT;

        Chunk c = new Chunk("Help Me Please", fb8);

        string rtnadd = "\r\n123 iTextSharp Rd\r\nHelpMe, ST 12345\r\n\r\nstackoverflow.com";

        Phrase pra = new Phrase(rtnadd, f8);

        Paragraph p = new Paragraph();
        p.SetLeading(1f, 1.1f);
        p.Add(c);
        p.Add(pra);

        tblra.TotalWidth = clsPDF.GetLongestWidth(p) + ra.PaddingLeft + ra.PaddingRight + 2;

        ra.AddElement(p);

        tblra.AddCell(ra);

        PdfPCell dummy = new PdfPCell();
        dummy.PaddingBottom = 0f;
        dummy.Border = iTextSharp.text.Rectangle.NO_BORDER;
        dummy.AddElement(tblra);

        mas_tbl.AddCell(dummy);

        //create "content" table
        PdfPTable t2 = clsPDF.CreateTable(1, false, Element.ALIGN_JUSTIFIED, cell_padding_bottom: 0f);

        //create FileStream for the file
        using (FileStream fs = new FileStream(strpath, FileMode.Create))
        {
            //get an instance of a PdfWriter, attached to the FileStream
            PdfWriter.GetInstance(doc, fs);

            //open the document
            doc.Open();

            string tmp = "";

            for (int i = 0; i < 80; i++)
            {
                tmp += "The brown fox jumped over the lazy dog a whole bunch of times." + i.ToString();
            }

            Phrase p2 = new Phrase(tmp, f10);

            t2.AddCell(p2);

            p2 = new Phrase("Another paragraph", f10);

            t2.AddCell(p2);

            tmp = "";

            PdfPTable t3 = clsPDF.CreateTable(1, false, cell_padding_bottom: 0f);

            for (int i = 0; i < 150; i++)
            {
                tmp += "The lazy dog didn't like that very much." + i.ToString();
            }

            t3.AddCell(new Phrase(tmp, f10));

            t2.AddCell(t3);

            t2.AddCell(new Phrase("I SURE HOPE THIS WORKED", f10));

            PdfPCell c2 = new PdfPCell();
            c2.PaddingBottom = 0f;
            c2.Border = iTextSharp.text.Rectangle.NO_BORDER;
            c2.Colspan = mas_tbl.NumberOfColumns;
            c2.AddElement(t2);

            mas_tbl.AddCell(c2);

            //work with adding a footer
            //FOOTER MSE:  ADD ENOUGH PADDING TO PUSH THE FOOTER TO THE BOTTOM OF THE PAGE
            Paragraph fp = new Paragraph(new Phrase("Line 1 of footer\r\nLine 2 of footer\r\nhere's more of my footer text:\r\nthis project was SOOOO much fun\r\nand stuff", fb8));

            //get the height of the footer
            float footer_height = clsPDF.GetTotalHeightOfParagraph(fp);

            Console.WriteLine("Footer height {0}", footer_height.ToString());

            //get the total amount of "writeable" space per page
            //(taking top and bottom margins into consideration)
            float avail = doc.PageSize.Height - (doc.TopMargin + doc.BottomMargin);

            //declare a variable to assist in calculating
            //the total amount of "writeable" room remaining
            //on the last page;
            //start with with the current height of the master table
            //(will do math below to calculate just what it's using
            // on the last page)
            float mas_tbl_last_page_height = mas_tbl.TotalHeight;

            //the purpose of this loop is to start determining
            //how much writeable space is left on the last page;
            //this loop will subtract the "available" value from
            //the total height of the master table until what's
            //left is the amount of space the master table is 
            //using on the last page of the document only
            while (mas_tbl_last_page_height > avail)
            {
                mas_tbl_last_page_height -= avail;
            }

            //to truly calculate the amount of writeable space
            //remaining, subtract the amount of space that the
            //master table is utilizing on the last page of
            //the document, from the total amount of writeable
            //space per page
            float room_remaining = avail - mas_tbl_last_page_height;

            //declare variable for the padding amount
            //that will be used above the footer
            float pad_amt = 0f;

            if (room_remaining > (footer_height * 2))
            {
                //pad to push down
                pad_amt = room_remaining - (footer_height * 2);
            }
            else
            {
                //don't use a pad
                //(just let the table wrap normally)
                pad_amt = 0f;
            }

            //declare the footer cell, and set all of it's values
            PdfPCell ftcell = new PdfPCell();
            ftcell.HorizontalAlignment = Element.ALIGN_JUSTIFIED;

            //(use column span that is equal to the number of
            // columns in the master table)
            ftcell.Colspan = mas_tbl.NumberOfColumns;

            ftcell.Border = iTextSharp.text.Rectangle.NO_BORDER;
            ftcell.PaddingTop = pad_amt;
            ftcell.PaddingBottom = 0f;
            ftcell.AddElement(fp);

            //add the footer cell to the master table
            mas_tbl.AddCell(ftcell);

            //add the master table to the document, which should contain everything
            doc.Add(mas_tbl);

            //close the document
            doc.Close();



//HELPER METHODS
internal static PdfPTable CreateTable(int column_count,
                                      bool include_border,
                                      int h_align = Element.ALIGN_LEFT,
                                      int v_align = Element.ALIGN_TOP,
                                      float leading_multiplier = 1.1f,
                                      float total_width = 468f,
                                      float cell_padding_bottom = 5f,
                                      float cell_padding_top = 0)
{
    ////////////////////////////////////////////////////////////////////////////////////
    PdfPTable t = new PdfPTable(column_count);

    //this line will keep the inner tables, 
    //from splitting off onto a 2nd page
    //(making them only do it on wrapping)
    //*NOTE:  this needs to be tested thoroughly
    //        to make sure that rows aren't dropped!!!
    t.SplitLate = false;

    //used if you're adding paragraphs directly to table cells,
    //instead of adding new PdfPCell objects
    if (include_border == false)
    {
        t.DefaultCell.Border = Rectangle.NO_BORDER;
    }

    t.DefaultCell.PaddingLeft = 0f;

    t.DefaultCell.PaddingRight = 0f;

    t.DefaultCell.PaddingTop = cell_padding_top;

    t.DefaultCell.PaddingBottom = cell_padding_bottom;

    t.DefaultCell.HorizontalAlignment = h_align;

    t.DefaultCell.VerticalAlignment = h_align;

    t.TotalWidth = total_width;

    t.LockedWidth = true;

    t.DefaultCell.SetLeading(0, leading_multiplier);

    return t;
}

internal static float GetLongestWidth(Paragraph p)
{

    List<float> f = new List<float>();

    foreach (Chunk c in p.Chunks)
    {
        string[] strarray = c.Content.Split(new string[] { "\r\n" }, System.StringSplitOptions.None);
        for (int i = 0; i < strarray.Length; i++)
        {
            Chunk tc = new Chunk(strarray[i], c.Font);
            Console.WriteLine(tc.Content + ", width: {0}", tc.GetWidthPoint().ToString());
            f.Add(tc.GetWidthPoint());
        }
    }

    return f.Max();

}

internal static float GetTotalHeightOfParagraph(Paragraph p)
{

    PdfPTable t = clsPDF.CreateTable(1, false, cell_padding_bottom: 0f);

    t.DefaultCell.PaddingBottom = 0f;
    t.DefaultCell.PaddingTop = 0f;

    t.AddCell(p);

    return t.TotalHeight;

}

用绝对定位法解决了这个问题。我通过将页脚高度添加到底部边距的值来计算页脚应该位于的位置。为了减轻对覆盖现有内容的担忧,我计算最后一页上剩余的可用空间量;如果我的页脚还不够,我会添加一个新页面,并在下一页的开头发布我的内容

        byte[] content;

        using (MemoryStream output = new MemoryStream())
        {
            PdfReader pdf_rdr = new PdfReader(strpath);

            PdfStamper stamper = new PdfStamper(pdf_rdr, output);

            PdfContentByte pcb = stamper.GetOverContent(pdf_rdr.NumberOfPages);

            PdfPTable ftbl = clsPDF.CreateTable(1, false, cell_padding_bottom: 0f);

            Paragraph fp = new Paragraph(new Phrase("Line 1 of footer\r\nLine 2 of footer\r\nhere's more of my footer text:\r\nthis project was SOOOO much fun\r\nand stuff", fb8));

            //get the height of the footer
            float footer_height = clsPDF.GetTotalHeightOfParagraph(fp);

            Console.WriteLine("Footer height {0}", footer_height.ToString());

            //get the total amount of "writeable" space per page
            //(taking top and bottom margins into consideration)
            float avail = doc.PageSize.Height - (doc.TopMargin + doc.BottomMargin);

            //declare a variable to assist in calculating
            //the total amount of "writeable" room remaining
            //on the last page;
            //start with with the current height of the master table
            //(will do math below to calculate just what it's using
            // on the last page)
            float mas_tbl_last_page_height = mas_tbl.TotalHeight;

            mas_tbl_last_page_height = mas_tbl_last_page_height % avail;

            avail = avail - mas_tbl_last_page_height;

            Console.WriteLine(clsPDF.GetTotalHeightOfParagraph(fp).ToString());

            //float ft_top = avail - mas_tbl_last_page_height - clsPDF.GetTotalHeightOfParagraph(fp) - doc.BottomMargin;
            float ft_top = doc.BottomMargin + clsPDF.GetTotalHeightOfParagraph(fp);

            ftbl.AddCell(fp);

            if (avail < clsPDF.GetTotalHeightOfParagraph(fp))
            {
                stamper.InsertPage(pdf_rdr.NumberOfPages + 1, pdf_rdr.GetPageSize(1));
                pcb = stamper.GetOverContent(pdf_rdr.NumberOfPages);
                ft_top = doc.PageSize.Height - doc.TopMargin;
            }

            ftbl.WriteSelectedRows(0, -1, doc.LeftMargin, ft_top, pcb);

            // Set the flattening flag to true, as the editing is done
            stamper.FormFlattening = true;

            // close the pdf stamper
            stamper.Close();

            //close the PDF reader
            pdf_rdr.Close();

            content = output.ToArray();

        }

        //write the content to a PDF file
        using (FileStream fs = File.Create(strpath))
        {
            fs.Write(content, 0, (int)content.Length);
            fs.Flush();
        }

根据我的经验,iTextSharp中的渲染引擎非常糟糕,PageEvents是唯一真正的信号发送方式。我希望你能认识到iTextSharps的局限性。我从另一个角度来看,这不是一个糟糕的引擎问题,而是对设计的理解不够:直接的内容是把东西放在绝对位置;如果你想绝对定位某些内容,请使用直接内容;如果要在每一页上都添加一些内容,请使用页面事件。但结果和Paul的一样:使用页面事件和直接内容。使用直接内容将是一个可接受的解决方案,只将我的页脚绝对放在最后一页上。然而,由于我的内容是可变长度的,我如何知道最后一页上还有多少空间,以便确定是否有足够的空间容纳它?