Question

How to add the total page number on every page with iText?

Was it helpful?

Solution

  1. Process the output from a PdfWriter to a bytestream first with a dummy page count.
  2. Create a PdfReader from that bytestream, calling PdfReader.getNumberOfPages to get the actual page count.
  3. Recreate the PDF output, knowing what the page count will be, changing the footer accordingly.

It's messy, but there's no easy way to know the page count without a two-pass approach. See the example code for details on manipulating PDFs.

OTHER TIPS

You can create a class that inherits from PdfPageEventHelper then override theses two functions like this :

Imports System.Collections.Generic
Imports System.Text

Imports iTextSharp.text.pdf
Imports iTextSharp.text

Namespace PDF_EnteteEtPiedDePage
    Public Class EnteteEtPiedDePage
        Inherits PdfPageEventHelper
        ' This is the contentbyte object of the writer
        Private cb As PdfContentByte

        ' we will put the final number of pages in a template
        Private template As PdfTemplate

        ' this is the BaseFont we are going to use for the header / footer
        Private bf As BaseFont = Nothing

        ' This keeps track of the creation time
        Private PrintTime As DateTime = DateTime.Now

        ' we override the onOpenDocument method
        Public Overrides Sub OnOpenDocument(writer As PdfWriter, document As Document)
            Try
                PrintTime = DateTime.Now
                bf = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED)
                cb = writer.DirectContent
                template = cb.CreateTemplate(50, 50)
            Catch de As DocumentException
            Catch ioe As System.IO.IOException
            End Try
        End Sub

        Public Overrides Sub OnStartPage(writer As PdfWriter, document As Document)
            MyBase.OnStartPage(writer, document)

            Dim pageSize As Rectangle = document.PageSize

        End Sub

        Public Overrides Sub OnEndPage(writer As PdfWriter, document As Document)
            MyBase.OnEndPage(writer, document)

            Dim pageN As Integer = writer.PageNumber
            Dim text As [String] = "Page " & pageN & " de "
            Dim len As Single = bf.GetWidthPoint(text, 8)

            Dim pageSize As Rectangle = 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, "Imprimé le : " & PrintTime.ToShortDateString() & " à " & PrintTime.ToShortTimeString, pageSize.GetRight(40), pageSize.GetBottom(30), 0)
            cb.EndText()
        End Sub

        Public Overrides Sub OnCloseDocument(writer As PdfWriter, document As Document)
            MyBase.OnCloseDocument(writer, document)

            template.BeginText()
            template.SetFontAndSize(bf, 8)
            template.SetTextMatrix(0, 0)
            template.ShowText("" & Convert.ToString((writer.PageNumber - 1)))
            template.EndText()
        End Sub

    End Class
End Namespace

Then after that just set the value of your pdfwriter PageEvent like this :

Dim PageEventHandler = New EnteteEtPiedDePage()
            aPdfWriter.PageEvent = PageEventHandler

No more searching, here is the solution. If they remove this link, I will post the code to my site and host the link.

iText page numbers example

Here is the code I used. It does not add to much overhead to write the pages to the output.

outputStream = new ByteArrayOutputStream();
output = new DataOutputStream(outputStream);
document = new Document();
writer = PdfWriter.getInstance(document, output);
document.open();
contentByte = writer.getDirectContent();
....add stuff
document.close();
writer.close();
byte[] output = outputStream.toByteArray();
PdfReader reader = new PdfReader(output);
//reset the output
outputStream = new ByteArrayOutputStream();
output = new DataOutputStream(outputStream);
document = new Document();
writer = PdfWriter.getInstance(document, output);
document.open();
PdfStamper stamper = new PdfStamper(reader, outputStream);
//add the pages
for (int i = 1; i <= pageCount; i++)
{
    contentByte = stamper.getOverContent(i);
    addParagraph("Page " + i + " of " + pageCount, new Point(500, 30), boldTextFont);  // my own paragraph font
}
stamper.close();

From a quick web search to remind myself of this; take a look at

Example

The relevant methods are onEndPage() to produce the "X" and onCloseDocument() to produce the "Y" once you hit the end of the document.

Here is a handy Function! (Based on Milhous's approach) (this uses itext version 4.1.6.0)

public static byte[] AddPageNumbers(byte[] pdf)
        {
            PdfReader reader = new PdfReader(pdf);
            var Pages = reader.NumberOfPages;
            MemoryStream ms = new MemoryStream();

            PdfStamper stamper = new PdfStamper(reader, ms);
            for (int i = 1; i <= Pages; i++)
            {
                PdfContentByte overContent;
                Font Signature = FontFactory.GetFont("Calibiri", 9, iTextSharp.text.Font.NORMAL, Color.BLACK);
                overContent = stamper.GetOverContent(i);
                var helv = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
                overContent.SaveState();
                overContent.BeginText();
                overContent.SetFontAndSize(helv, 10.0f);
                overContent.SetTextMatrix(PageSize.LETTER.Width / 2 - 20, PageSize.LETTER.Height - (PageSize.LETTER.Height - 20));
                overContent.ShowText("Page " + (i) + " of " + Pages);
                overContent.EndText();
                overContent.RestoreState();
            }
            stamper.Close();            
            return ms.ToArray();
        }

Try this because i was also suffering from this and i also new this library so after lot of trouble i got the result just only try to apply logic twice as i mentioned in comments.

class PdfReportServlet extends HttpServlet
{
    public static int total = 0;

    public static int getTotal() 
    {
        return total;
    }

    public static void setTotal(int total) {
        PdfReportServlet .total = total;
    }


    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
    {
        String reportFor = request.getParameter("report");
        if(!"".equals(reportFor))
        {
            if(reportFor.equals("pdf"))
            {
    /* 
       from here logic starts
    */
                response.setContentType("application/pdf");
                Document document = new Document(PageSize.LETTER.rotate());
                try
                {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    PdfWriter pdfWriter = PdfWriter.getInstance(document, bos);
                    PdfReport pdfReport = new PdfReport();
                    pdfWriter.setPageEvent(pdfReport);
                    document.open();
                    String id = request.getParameter("id");
                    DettagliFamigliaPDFReport.generatePDFReport(document,id);
                    document.close();

                    /*
                     * now again give new references to Document and PdfWriter classes.
                     */

                    document = new Document(PageSize.LETTER.rotate());

                    pdfWriter = PdfWriter.getInstance(document, response.getOutputStream());
                    PDFReport.setTotalPages(getTotal());/*##PAGES_TOTAL here i set total Pages which i get from above logic */
                    PdfReport pdfReport = new PdfReport();
                    pdfWriter.setPageEvent(pdfReport);
                    document.open();
                    id = request.getParameter("id");
                    PDFReport.generatePDFReport(document,id);
                    document.close();

                }
                catch(DocumentException de){
                    de.printStackTrace();
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }


}


/*
    Here below i write the class inherits the PdfPageEventHelper class and also having generatePDFReport() method.
*/


public class PDFReport extends PdfPageEventHelper
{
    public static PdfTemplate template;
    public static int totalPages=0;

    public static int getTotalPages() {
        return totalPages;
    }

    public static void setTotalPages(int totalPages) {
        DettagliFamigliaPDFReport.totalPages = totalPages;
    }


    public static void generatePDFReport(Document document,String id)
    {
        // here your data to write on page of pdf.
    }

    @Override
    public void onOpenDocument(PdfWriter writer, Document doc) 
    {
        template = writer.getDirectContent().createTemplate(100, 100);
    }

    int totalPage = 0;
    @Override
    public void onCloseDocument(PdfWriter writer, Document doc) 
    {
        totalPage = writer.getPageNumber() - 1;/* at the end this method calls and you will get total number of pages.*/
        PdfReportServlet.setTotal(totalPage); /* while first time logic of servlet executes then i set the total pages to servlet's variable using this logic.
and second time servlet's logic is executing i passed totalPages to this class variable
where i marked ##PAGES_TOTAL  */
    }

    @Override
    public void onStartPage(PdfWriter writer, Document doc) {
        // Here i write header logic when each time page starts.
    }

    @Override`enter code here`
    public void onEndPage(PdfWriter writer, Document doc) 
    {
        Rectangle rect = writer.getPageSize();
        float width = rect.getWidth()/2;   

        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
        Date date = new Date();

        String footerOne = "This document is printed on date "+dateFormat.format(date)+" - Page. "+writer.getPageNumber()+" of "+getTotalPages();

        Paragraph paraOne = new Paragraph(footerOne);

        Font fontFooter = new Font();
        fontFooter.setSize(8);
        fontFooter.setColor(Color.gray);

        paraOne.setFont(fontFooter);

        Phrase footerPhraseOne = new Phrase(paraOne);
        ColumnText.showTextAligned(writer.getDirectContent(),
                Element.ALIGN_CENTER, footerPhraseOne,
                width, 30, 0);
    }
}

This is a snippet of your code:

final PdfWriter writer = PdfWriter.getInstance(document, outputStream);
writer.setPageEvent(new PageStamper());

And the numbering code:

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import com.itextpdf.tool.xml.*;
import com.itextpdf.tool.xml.css.*;

import java.io.IOException;

/**
 * Adds page number to al the pages except the first.
 */
public class PageStamper extends PdfPageEventHelper {
    private static final Logger logger = Logger.getLogger(PageStamper.class);

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        final int currentPageNumber = writer.getCurrentPageNumber();

        if (currentPageNumber == 1) {
            return;
        }

        try {
            final Rectangle pageSize = document.getPageSize();
            final PdfContentByte directContent = writer.getDirectContent();

            directContent.setColorFill(BaseColor.GRAY);
            directContent.setFontAndSize(BaseFont.createFont(), 10);

            directContent.setTextMatrix(pageSize.getRight(40), pageSize.getBottom(30));
            directContent.showText(String.valueOf(currentPageNumber));

        } catch (DocumentException | IOException e) {
            logger.error("PDF generation error", e);
        }
    }
}

This code was found here: http://blog.abelsky.com/2014/01/22/adding-page-number-to-itext-generated-pdf/. Thank you so much, andy722

As the other answers indicate, you will first need to create the pdf, then look how many pages the pdf you created contains and then add the footer to each page. The ideal class to do this with is PdfStamper, which purpose is to add additional elements to an existing pdf/existing pdf pages.

Here is an elaborate example based on the example of Milhous:

Font smallFont = FontFactory.getFont("Arial", 9, Font.NORMAL);
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
Document document = new Document(PageSize.A4, 70, 70, 40, 40);
PdfWriter pdfWriter = PdfWriter.getInstance(document, pdfOutputStream);

document.open();
//CREATE PDF HERE - ADD CONTENT TO DOCUMENT
document.close();

int pageCount = pdfWriter.getPageNumber()-1;
byte[] pdfAsBytes = pdfOutputStream.toByteArray();

//add footer
PdfReader reader = new PdfReader(pdfAsBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DataOutputStream output = new DataOutputStream(outputStream);
document = new Document();
document.open();
PdfStamper stamper = new PdfStamper(reader, output);
for (int i = 1; i <= pageCount; i++) {
  ColumnText.showTextAligned(stamper.getOverContent(i), 
    Element.ALIGN_CENTER, new Phrase(i+"/" + pageCount, smallFont), 550, 30, 0);
}
stamper.close();
byte[] finalPdfAsBytes = outputStream.toByteArray();

This is the Rama code converted in Java (part of it at least)

public class test extends PdfPageEventHelper{
    private int _pg = 0;
    private BaseFont font;

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        _pg++;
        PdfContentByte cb = writer.getDirectContent();
        cb.beginText();
        try {
            Rectangle pageSize = document.getPageSize();
            cb.setFontAndSize(font, 8);
            cb.setTextMatrix(pageSize.getLeft(40), pageSize.getBottom(15));
            String s = "Page " + _pg + "/";
            cb.showText(s);
            cb.addTemplate(template, pageSize.getLeft(40) + font.getWidthPoint(s, 8), pageSize.getBottom(15));
        } catch (Exception exc) {
            logger.warn("got Exception : " + exc.getMessage());
        }
        cb.endText();
    }

    @Override
    public void onOpenDocument(PdfWriter writer, Document document) {
        super.onOpenDocument(writer, document);
        template = writer.getDirectContent().createTemplate(50, 50);
        try {
            font = BaseFont.createFont(BaseFont.COURIER, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
        } catch (Exception exc) {
            logger.warn("got Exception : " + exc.getMessage());
        }
    }

    @Override
    public void onCloseDocument(PdfWriter writer, Document document) {
        super.onCloseDocument(writer, document);

        template.beginText();
        try {
            template.setFontAndSize(font, 8);
            template.setTextMatrix(0f, 0f);
            template.showText("" + (writer.getPageNumber() - 1));
        } catch (Exception ex) {
            logger.error(ex);
        }
        template.endText();
    }

}

After two days effort! I finally get total numbers of pages count in the PDF. Actually my client requirement was to show footer only on last page of the PDF. That's how i did this, hope it will help someone in future. Step 1: Create new class that will inherit from PdfPageEventHelper just like this.

public class ITextEvents : PdfPageEventHelper
{
public int pageNos { get; set; }

/// <summary>
/// CALLED ON EACH PAGE END PAGE, 
/// </summary>
/// <param name="writer"></param>
/// <param name="document"></param>
public override void OnEndPage(iTextSharp.text.pdf.PdfWriter writer, iTextSharp.text.Document document)
{
//LOGIC: TO SHOW DISCLAIMER ONLY ON LAST PAGE
if (document.PageNumber == pageNos)
{
//Create new Paragraph
Paragraph p = new Paragraph();
//ADD CHUNK IN PARAGRAPH
p.Add(new Chunk("Heading-Text: ", FontFactory.GetFont("Calibri",11,Font.BOLD)));
p.Add(new Chunk("Details sow in footer", FontFactory.GetFont("Calibri", 10)));
//PARAGRAPH TEXT ALIGNMENT. 
p.Alignment = Element.ALIGN_LEFT;
//CREATE PDF TABLE, FOR ADDING NEW CELL AND ROW IN THE PDF
PdfPTable footerTbl = new PdfPTable(1);
//SETING TABLE WIDTH
footerTbl.TotalWidth = 580;
footerTbl.HorizontalAlignment = Element.ALIGN_LEFT;
//ADDING CELL IN THE TABLE
PdfPCell cell = new PdfPCell(p);
//CELL LAYOUT DESIGN
cell.Border = 1;
cell.PaddingLeft = 10;
cell.PaddingRight = 10;
footerTbl.AddCell(cell);
//ADDING TABLE IN THE PDF
footerTbl.WriteSelectedRows(0, -1, 10, 110, writer.DirectContent);
}
}

/// <summary>
/// THIS METHOD WILL GET TOTAL PDF PAGES COUNT,
/// REQUIREMENT OF THIS METHOD WAS IS TO SHOW FOOTER ON LAST PAGE IN THE PDF. 
/// </summary>
/// <param name="HistoryHTML"></param>
/// <param name="Name"></param>
/// <param name="filePath"></param>
/// <returns></returns>
public int GetTotalPageCount(string HistoryHTML, String Name, string filePath)
{
try
{
using (MemoryStream stream = new System.IO.MemoryStream())
{
StringReader sr = new StringReader(HistoryHTML);
Document pdfDoc = new Document(PageSize.A4, 30f, 30f, 30f, 90f);
PdfWriter writer = PdfWriter.GetInstance(pdfDoc, stream);
pdfDoc.Open();
XMLWorkerHelper.GetInstance().ParseXHtml(writer, pdfDoc, sr);
pdfDoc.Close();

bool exists = System.IO.Directory.Exists(filePath);
if (!exists)
    System.IO.Directory.CreateDirectory(filePath);
System.IO.File.WriteAllBytes((filePath + @"\FileName-" + Name + ".pdf"), stream.ToArray());

string path = AppDomain.CurrentDomain.BaseDirectory + @"FolderPath\" + "FileName-" + Name + ".pdf";

PdfReader pdfReader = new PdfReader(path);
int numberOfPages = pdfReader.NumberOfPages;
pdfReader.Close();
stream.Close();
return numberOfPages;

}
}
catch (Exception ex)
{
CommonFunction.logElmahError(ex);
}
return -1;
}
}  

Before generating pdf file from stream, call this method to get total page count.

int pageCount = new Common.ITextEvents().GetTotalPageCount(HistoryHTML, Name, Server.MapPath("~/FolderPath"));

For a better understanding I am providing controller side code.

public string ExportPDF(string HistoryHTML, String Name)
        {
            int pageCount = new Common.ITextEvents().GetTotalPageCount(HistoryHTML, Name, Server.MapPath("~/FolderPath"));

            using (MemoryStream stream = new System.IO.MemoryStream())
            {
                StringReader sr = new StringReader(HistoryHTML);
                Document pdfDoc = new Document(PageSize.A4, 30f, 30f, 30f, 30f);
                PdfWriter writer = PdfWriter.GetInstance(pdfDoc, stream);
                pdfDoc.Open();

                //APPEND DISCLAIMER TEXT ACCORING TO HIPAA RULES.
                writer.PageEvent = new Common.ITextEvents() { pageNo = pageCount };

                XMLWorkerHelper.GetInstance().ParseXHtml(writer, pdfDoc, sr);
                pdfDoc.Close();

                bool exists = System.IO.Directory.Exists(Server.MapPath("~/FolderPath"));
                if (!exists)
                    System.IO.Directory.CreateDirectory(Server.MapPath("~/FolderPath"));
                System.IO.File.WriteAllBytes(Server.MapPath("~/FolderPath/" + Name + ".pdf"), stream.ToArray());

                return File(stream.ToArray(), "application/pdf", "FileName-" + Name + "-" + Name + ".pdf");
            }
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top