How to handle the case in wich an iText\\iTextShar

2019-02-27 20:46发布

问题:

I have the following problem using iTextSharp.

I am putting some tables into my document. The problem is that if a table content not fit on a page and go into another page I have that it overwrite the second page header, so I have the following situation:

As you can see I am inserting a table at the end of a page and this is splitted in two pages and the second page header is overwrite by the table content.

I want to avoid this situation but I don't know how have I to do.

I am thinking that maybe I can do one of the following things:

1) Maybe I can check if an element complitly enter into a page. If not create a new page and put it into this new page (but this can be a problem if a single table need more then one page, in the case if I have a very big table)

2) I allow that a table is splitted in 2 pages but in this case I left some margin spacing in the upper of the second page so the header is correctly shown.

Or what can I do to solve this situation?

Tnx

EDIT:

I have add the header in the following way:

1) I have implement create a class named PdfHeaderFooter that extends the PdfPageEventHelper class and that implements its methods.

At this time its class contains the following methods:

    // write on start of each page
    public override void OnStartPage(PdfWriter writer, Document document)
    {
        base.OnStartPage(writer, document);
        PdfPTable tabHead = new PdfPTable(3);
        tabHead.SetWidths(new int[] { 165, 205, 125 });

        //tabHead.TotalWidth = 460F;
        tabHead.TotalWidth = document.Right - document.Left;        // TotalWidth = 495
        tabHead.WidthPercentage = 98;


        PdfPCell cell1 = new PdfPCell(iTextSharp.text.Image.GetInstance(folderImages + "logoEarlyWarning.png"), true) { Border = PdfPCell.BOTTOM_BORDER };
        tabHead.AddCell(cell1);
        //tabHead.AddCell(new PdfPCell(new Phrase("CELL 1:")) { Border = PdfPCell.BOTTOM_BORDER, Padding = 5, MinimumHeight = 50, PaddingTop = 15, });

        tabHead.AddCell(new PdfPCell(new Phrase("")) { Border = PdfPCell.BOTTOM_BORDER, Padding = 5, MinimumHeight = 50, PaddingTop = 15 });

        if(_sourceId == "NVD")
        {
            iTextSharp.text.Image logo = iTextSharp.text.Image.GetInstance(folderImages + "nvdLogo.png");
            logo.ScalePercent(48f);
            //PdfPCell cell3 = new PdfPCell(iTextSharp.text.Image.GetInstance(folderImages + "nvdLogo.png"), true) { Border = PdfPCell.BOTTOM_BORDER, PaddingBottom = 25 };
            PdfPCell cell3 = new PdfPCell(logo) { Border = PdfPCell.BOTTOM_BORDER, PaddingLeft = 50 };
            tabHead.AddCell(cell3);
        }
        else if(_sourceId == "DeepSight")
        {
            PdfPCell cell3 = new PdfPCell(iTextSharp.text.Image.GetInstance(folderImages + "DSLogo.jpg"), true) { Border = PdfPCell.BOTTOM_BORDER };
            tabHead.AddCell(cell3);
        }
        //tabHead.AddCell(new PdfPCell(new Phrase("CELL 3:")) { Border = PdfPCell.BOTTOM_BORDER, Padding = 5, MinimumHeight = 50, PaddingTop = 15 });


        tabHead.WriteSelectedRows(0, -1, document.Left, document.Top, writer.DirectContent);
    }

    // write on end of each page
    public override void OnEndPage(PdfWriter writer, Document document)
    {
        base.OnEndPage(writer, document);

        int pageN = writer.PageNumber;      // numero della pagina corrente OK
        String text = "Page " + pageN + " of ";
        float len = bf.GetWidthPoint(text, 8);

        Rectangle pageSize = document.PageSize;

        cb.SetRGBColorFill(100, 100, 100);

        cb.BeginText();
        cb.SetFontAndSize(bf, 8);
        cb.SetTextMatrix(pageSize.GetLeft(40), pageSize.GetBottom(30));
        cb.ShowText(text);
        cb.EndText();

        cb.AddTemplate(template, pageSize.GetLeft(40) + len, pageSize.GetBottom(30));

        cb.BeginText();
        cb.SetFontAndSize(bf, 8);
        cb.ShowTextAligned(PdfContentByte.ALIGN_RIGHT,
            "Printed On " + PrintTime,
            pageSize.GetRight(40),
            pageSize.GetBottom(30), 0);
        cb.EndText();
    }

So ad you can see, the OnStartPage() method add the header at the beginning of each pages and the OnEndPage() add the footer at the end of each pages.

So from what I understand from your response I have to do the followings steps:

1) Moove the header insertion from OnStartPage() to OnEndPage()

2) Use an absolut position for place it in the upper part of the pages.

3) In the document creation use the header heigt to set the top margin.

Is it right?

EDIT 2:

I tryied to do as you say to me and now I have the following situations:

1) Into my PdfHeaderFooter that extends PdfPageEventHelper I have deleted the OnStartPage() method

2) I have moove the header table creation into the OnEndPage() method that now it this one:

    // write on end of each page
    public override void OnEndPage(PdfWriter writer, Document document)
    {
        base.OnEndPage(writer, document);

        // HEADER:
        PdfPTable tabHead = new PdfPTable(3);
        tabHead.SetWidths(new int[] { 165, 205, 125 });

        //tabHead.TotalWidth = 460F;
        tabHead.TotalWidth = document.Right - document.Left;        // TotalWidth = 495
        tabHead.WidthPercentage = 98;

        PdfPCell cell1 = new PdfPCell(iTextSharp.text.Image.GetInstance(folderImages + "logoEarlyWarning.png"), true) { Border = PdfPCell.BOTTOM_BORDER };
        tabHead.AddCell(cell1);
        //tabHead.AddCell(new PdfPCell(new Phrase("CELL 1:")) { Border = PdfPCell.BOTTOM_BORDER, Padding = 5, MinimumHeight = 50, PaddingTop = 15, });

        tabHead.AddCell(new PdfPCell(new Phrase("")) { Border = PdfPCell.BOTTOM_BORDER, Padding = 5, MinimumHeight = 50, PaddingTop = 15 });

        if (_sourceId == "NVD")
        {
            iTextSharp.text.Image logo = iTextSharp.text.Image.GetInstance(folderImages + "nvdLogo.png");
            logo.ScalePercent(48f);
            //PdfPCell cell3 = new PdfPCell(iTextSharp.text.Image.GetInstance(folderImages + "nvdLogo.png"), true) { Border = PdfPCell.BOTTOM_BORDER, PaddingBottom = 25 };
            PdfPCell cell3 = new PdfPCell(logo) { Border = PdfPCell.BOTTOM_BORDER, PaddingLeft = 50 };
            tabHead.AddCell(cell3);
        }
        else if (_sourceId == "DeepSight")
        {
            PdfPCell cell3 = new PdfPCell(iTextSharp.text.Image.GetInstance(folderImages + "DSLogo.jpg"), true) { Border = PdfPCell.BOTTOM_BORDER };
            tabHead.AddCell(cell3);
        }
        //tabHead.AddCell(new PdfPCell(new Phrase("CELL 3:")) { Border = PdfPCell.BOTTOM_BORDER, Padding = 5, MinimumHeight = 50, PaddingTop = 15 });


        tabHead.WriteSelectedRows(0, -1, document.Left, document.Top, writer.DirectContent);

        float headerHeight = tabHead.CalculateHeights();



        // FOOTER:
        int pageN = writer.PageNumber;      // numero della pagina corrente OK
        String text = "Page " + pageN + " of ";
        float len = bf.GetWidthPoint(text, 8);

        Rectangle pageSize = document.PageSize;

        cb.SetRGBColorFill(100, 100, 100);

        cb.BeginText();
        cb.SetFontAndSize(bf, 8);
        cb.SetTextMatrix(pageSize.GetLeft(40), pageSize.GetBottom(30));
        cb.ShowText(text);
        cb.EndText();

        cb.AddTemplate(template, pageSize.GetLeft(40) + len, pageSize.GetBottom(30));

        cb.BeginText();
        cb.SetFontAndSize(bf, 8);
        cb.ShowTextAligned(PdfContentByte.ALIGN_RIGHT,
            "Printed On " + PrintTime,
            pageSize.GetRight(40),
            pageSize.GetBottom(30), 0);
        cb.EndText();
    }

As you can see now the OnEndPage() method contains the header and footer creation.

It seems to me that tabHead (my header table) use the absolute positioning because I have:

tabHead.WriteSelectedRows(0, -1, document.Left, document.Top, writer.DirectContent);

In the other class where I create the document I have this line:

document = new iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 50, 50, 30, 65);

so the pages are A4 and have 30px as upper margin.

But I still have the same problem.

If I change the 30 value with 80 it simply moves down the header leaving a white top but does not solve the problem.

What am I missing? What is wrong?

回答1:

I assume that you are adding page headers correctly. That is: you have implemented the onEndPage() method in a page event (not the onStartPage() method) and you're adding the header at an absolute position. As you are adding the header at an absolute position, you know exactly how much space it takes.

When you create a document object, you define a page size. If you don't, the page size will be A4 (595 x 842 user units). You also define margins. If you don't, the margins will be half an inch (36 user units) on either side.

When a table splits, the page size and its margins are respected: iText won't put any part of the table in that area.

Hence the solution is simple: as you know the space needed by the header, all you have to do is to define the margin in a way that the header fits into the margin.

Update after the question was updated:

1.

This is wrong:

// write on start of each page
public override void OnStartPage(PdfWriter writer, Document document)
{
    ...
    tabHead.WriteSelectedRows(0, -1, document.Left, document.Top, writer.DirectContent);
}

You should never add any content in the OnStartPage() method. Move the code that writes the header to the OnEndPage() method.

2.

You are already using an absolute position to add tabHead: you are adding it at the coordinate x = document.left; y = document.Top.

You define a width percentage for a file that is added at an absolute position. That doesn't make any sense, remove the following line:

tabHead.WidthPercentage = 98;

However, you are wasting resources. For instance: you create the logo in the page event: iTextSharp.text.Image.GetInstance(folderImages + "nvdLogo.png"). This means that you are adding the image bytes as many times as you have pages, resulting in redundant info in the PDF file (you have a bloated file).

You can optimize the process by creating the images outside the onEndPage() method. You can even create the table outside that method. For instance: create a member variable tabHead in the event class and create the table either in the constructor of your event implementation or in the OnOpenDocument() method (that method only gets called once). Reuse the table (and the images) in the onEndPage() method.

Now the image bytes will be present in the PDF file only once (you'll gain plenty of KBytes) and you'll only have to create the table once (less CPU-time wasted).

3.

An even better solution is to create the tabHead object outside the page event and before you open the document. As you define a total width, you can ask the table for its height:

float h = tabHead.TotalHeight;

Now you can use h to define the top margin:

document.SetMargins(36f, 36f, h, 36f);

Note that it is important to set the margins before you open your document. You'll also have to adapt the coordinate at which you add the table. For instance:

tabHead.WriteSelectedRows(0, -1, document.Left, document.Top + h, writer.DirectContent);

Your comment regarding the position of the header revealed a serious lack of insight.