ralfstuckert / pdfbox-layout

MIT License
155 stars 74 forks source link

how to automatically create header and footer area when finishing a pdf page? #58

Closed arnthom closed 6 years ago

arnthom commented 6 years ago

Hi there!

When I finish a page and use ControlElement.NEWPAGE oder finally the last page is going to be rendered, how can I set header and footer areas? I have to switch from iText to a different library and pdfbox-layout has done fine up to now (even with some tricky column layout to build table-like structures). iText uses event-handling to add headers and footers. Maybe something similar exists in pdfbox-layout? Maybe with a DrawListener or RenderListener?

any help would be greatly appreciated!

arnthom commented 6 years ago

ok, found a hint in listener.pdf

arnthom commented 6 years ago

but I got a different question ... I'd like to add a watermark picture, preferably when using the renderlistener's afterPage method. But all I get is a

Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at rst.pdfbox.layout.elements.Document.render(Document.java:284)

ralfstuckert commented 6 years ago

You seem to change the (PDFLayout-)document's content during rendering. Could you provide a code sinppet?

arnthom commented 6 years ago

Hello Ralf, it seems, that I have tried to add some rendering stuff at too many places/sources. I think I have to remove all but one and look, if it I get some result ...

Now I use a RenderListener and the .afterPage method. This works fine, when a page is finished, the method is called and all the stuff (header, footer, ...) is done. BUT ... now I get a "Cannot read while there is an open stream writer" message when saving the document.

I can track it down to this: I have implemented a RenderListener with the "finishing stuff" like headers and footers. The methods beforePage and afterPage are called from the base class of all page defining classes. In that base class a RenderContext is created:

private RenderContext getRenderContext() throws IOException {
        if (rc == null) {
            rc = new RenderContext(doc, doc.getPDDocument());
        }
        return rc;
    }

With getRenderContext() I run the beforePage and afterPage methods. In this part, some stream is opened and never closed, so doc.save complains about it. If I do not use the RenderContext, no error mesage comes up and the PDF is created. When it is used, no PDF is created and the error is shown.

Is there anything I have to do with the RenderContext when closing/saving the document?

arnthom commented 6 years ago

I tried to avoid this problem and set header and footer within the page-writing classes ... but this leads to pagenumber 0 and (because there are some classes that do write onto several pages but only one) some sections of the pdf nave a header/footer just on the last page of the section. This is no workaround. I have to do it via listeners. But how do I overcome the problems above?

ralfstuckert commented 6 years ago

You may only do low-level rendering stuff in the RendererListener as shown in the listener example. You must neither alter the Document or create your own RenderContext, since it opens and maintains a PDPageContentStream.

Could you provide a simplified example reproducing the problem?

arnthom commented 6 years ago

Mmmh, to use my RenderListener, I have to call the afterPage method ... with what renderContext? Where do I get it from. Maybe the problem is, that I use a new RenderContext: when ending a text section in the pdf I call

    protected void teardownWrite() throws PdfCException {
        try {
            // Kopf- und Fußzeile schreiben
            getEvent().afterPage(getRenderContext());
        } catch (IOException e) {
            throw new PdfCException(e);
        }
    }
    private RenderContext rc; 
    private RenderContext getRenderContext() throws IOException {
        if (rc == null) {
            rc = new RenderContext(myController.getDocument(), myController.getPDDocument());
        }
        return rc;
    }

    private PdfEvent getEvent() throws PdfCException {
        if (event == null) {
            event = new PdfEvent(myController, myData, myController.getZeichensatz());
        }
        return event;
    }

with

public class PdfEvent implements RenderListener {
...
}

If the "new RenderContext(...)" is the problem, wherer do I get it from for the afterPage method?

arnthom commented 6 years ago

OOps, it may be told, that I tried to call afterPage() manually ... removing this, the method is called later automatically ... now I try to make it work as it should.

Thanks for giving the hints I needed!

arnthom commented 6 years ago

Finally ... done (needed a renderer as well as a renderListener) Thanks for your help!