itext7 end_page events are called when document is closed

旧城冷巷雨未停 提交于 2020-01-06 06:01:43

问题


I am trying to follow the example given in https://developers.itextpdf.com/examples/page-events/clone-page-events-headers-and-footers#2656-variableheader.java to create a PDF document with variable header. But the events are not fired correctly. Here is the code I have tested with -

class Program
{
    public static String DEST = "test.pdf";
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        manipulatePdf(DEST);
    }


    public static List<int> getFactors(int n)
    {
        List<int> factors = new List<int>();
        for (int i = 2; i <= n; i++)
        {
            while (n % i == 0)
            {
                factors.Add(i);
                n /= i;
            }
        }
        return factors;
    }

    protected static void manipulatePdf(String dest)
    {
        PdfDocument pdfDoc = new PdfDocument(new PdfWriter(DEST));
        Document doc = new Document(pdfDoc, PageSize.A4, true);
        VariableHeaderEventHandler handler = new VariableHeaderEventHandler();
        pdfDoc.AddEventHandler(PdfDocumentEvent.END_PAGE, handler);
        List<int> factors;
        for (int i = 2; i < 4; i++)
        {
            factors = getFactors(i);
            if (factors.Count == 1)
            {
                doc.Add(new Paragraph("This is a prime number!"));
            }
            foreach (int factor in factors)
            {
                doc.Add(new Paragraph("Factor: " + factor));
            }

            handler.setHeader(String.Format("THE FACTORS OF {0}", i));
            if (300 != i)
            {
                doc.Add(new AreaBreak());
            }
        }
        doc.Close();
    }


    protected class VariableHeaderEventHandler : IEventHandler
    {
        protected String header;

        public void setHeader(String header)
        {
            this.header = header;
        }

        public void HandleEvent(Event @event)
        {
            PdfDocumentEvent documentEvent = (PdfDocumentEvent)@event;
            try
            {
                new PdfCanvas(documentEvent.GetPage())
                        .BeginText()
                        .SetFontAndSize(PdfFontFactory.CreateFont(StandardFonts.HELVETICA), 12)
                        .MoveText(450, 806)
                        .ShowText(header)
                        .EndText()
                        .Stroke();
            }
            catch (IOException e)
            {
            }
        }
    }
}

If I run this code, all pages show header as "THE FACTORS OF 3". But they should show "THE FACTORS OF 2" for first page "THE FACTORS OF 3" for second page and "THE FACTORS OF 4" for third page. I am not sure how to fix it. Any suggestions?


回答1:


As already mentioned in an earlier answer iText 7 page events are triggered with some delay - generally not until the document is closed, though, as you assume in your question title, but page n+1 may indeed already be nearly finished before the event for page n is processed.

Thus, it does not suffice to set the new page header as attribute of the event handler, one also has to tell the event handler when to start using it. So ...

An improved handler

protected class ImprovedVariableHeaderEventHandler : IEventHandler
{
    Dictionary<PdfPage, String> headers = new Dictionary<PdfPage, string>();
    protected String header = "";

    public void setHeaderFor(String header, PdfPage page)
    {
        headers[page] = header;
    }

    public void HandleEvent(Event @event)
    {
        PdfDocumentEvent documentEvent = (PdfDocumentEvent)@event;
        PdfPage page = documentEvent.GetPage();
        if (headers.ContainsKey(page))
        {
            header = headers[page];
            headers.Remove(page);
        }
        new PdfCanvas(page)
                .BeginText()
                .SetFontAndSize(PdfFontFactory.CreateFont(StandardFonts.HELVETICA), 12)
                .MoveText(450, 806)
                .ShowText(header)
                .EndText()
                .Stroke();
    }
}

This improved handler can be used like this:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc, PageSize.A4, true);
ImprovedVariableHeaderEventHandler handler = new ImprovedVariableHeaderEventHandler();
pdfDoc.AddEventHandler(PdfDocumentEvent.END_PAGE, handler);
List<int> factors;
for (int i = 2; i < 40; i++)
{
    if (2 != i)
    {
        doc.Add(new AreaBreak());
    }

    factors = getFactors(i);
    if (factors.Count == 1)
    {
        doc.Add(new Paragraph("This is a prime number!"));
    }
    foreach (int factor in factors)
    {
        doc.Add(new Paragraph("Factor: " + factor));
    }

    handler.setHeaderFor(String.Format("THE FACTORS OF {0}", i), pdfDoc.GetLastPage());
}
doc.Close();

The result is the output you expected.

A variant with multiple header elements

We probably don't want to waste so many pages for so little content. In that case the factorizations of multiple numbers should fit on a single page and we would like to have all of them mentioned in the header. That can be achieved using an alternative event listener like this:

protected class ImprovedVariableHeaderEventHandlerAlt : IEventHandler
{
    Dictionary<PdfPage, String> headers = new Dictionary<PdfPage, string>();
    protected String header = "";

    public void addHeaderDetailFor(string header, PdfPage page)
    {
        if (headers.ContainsKey(page))
            headers[page] += ", " + header;
        else
            headers[page] = header;
    }

    public void HandleEvent(Event @event)
    {
        PdfDocumentEvent documentEvent = (PdfDocumentEvent)@event;
        PdfPage page = documentEvent.GetPage();
        if (headers.ContainsKey(page))
        {
            header = String.Format("THE FACTORS OF {0}", headers[page]);
            headers.Remove(page);
        }
        new PdfCanvas(page)
                .BeginText()
                .SetFontAndSize(PdfFontFactory.CreateFont(StandardFonts.HELVETICA), 12)
                .MoveText(150, 806)
                .ShowText(header)
                .EndText()
                .Stroke();
    }
}

using this code:

PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc, PageSize.A4, true);
ImprovedVariableHeaderEventHandlerAlt handler = new ImprovedVariableHeaderEventHandlerAlt();
pdfDoc.AddEventHandler(PdfDocumentEvent.END_PAGE, handler);
List<int> factors;
for (int i = 2; i < 40; i++)
{
    doc.Add(new Paragraph(String.Format("The factors of {0}", i)).SetBold());
    handler.addHeaderDetailFor(i.ToString(), pdfDoc.GetLastPage());

    factors = getFactors(i);
    if (factors.Count == 1)
    {
        doc.Add(new Paragraph("This is a prime number!"));
    }
    foreach (int factor in factors)
    {
        doc.Add(new Paragraph("Factor: " + factor));
    }
}
doc.Close();

By informing the event listener right after drawing the section header for a new number, the number will be mentioned in the page header of the page on which this section header is printed while the actual factorization might be on the following page.



来源:https://stackoverflow.com/questions/50445436/itext7-end-page-events-are-called-when-document-is-closed

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!