Multiple Renderers per iText document: Updated wit

2019-07-15 03:55发布

问题:

I am using iText7 to create a document which has multiple sections. Some of the sections are to be formatted normally, but some are to be formatted into columns. I can get it to format properly into columns by using the ColumnDocumentRenderer object, but when I do so, the entire document is set to use columns. Is there any way to have iText swap which renderer to use on the fly?

When I try to swap out renderers on the fly, I get a null pointer exception (com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:482)).

PdfDocument pdf = new PdfDocument(new PdfWriter(targetFile));
Document document = new Document(pdf);
DocumentRenderer defRender = new DocumentRenderer(document);
document.setRenderer(defRender);
ColumnDocumentRenderer dictRender = getColumnRender();

while (<CONDITION>) {
    document.setRenderer(dictRender);
    document.add(new Paragraph("THIS IS NORMAL TEXT"));
    document.add(new Paragraph("THIS IS NORMAL TEXT"));
    document.add(new Paragraph("THIS IS NORMAL TEXT"));
    <...> 
    document.setRenderer(defRender);
    document.add(new Paragraph("THIS IS COLUMN TEXT"));
    document.add(new Paragraph("THIS IS COLUMN TEXT"));
    document.add(new Paragraph("THIS IS COLUMN TEXT"));
    <...>
}

After I set the renderer to dictRender, the first document.add() statement throws a null pointer error at com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:482).

I don't want to have to create multiple different PDF files, but am thinking that might be what I end up having to do. Thanks for any help here.

回答1:

The exception occurs because page content is flushed as soon as it is possible by default to save memory.

To avoid immediate flushing of content, there is a parameter of DocumentRenderer and ColumnDocumentRenderer constructors.

So, for instance, to create a DocumentRenderer with immediate flushing switched off, you would have to create it as follows:

DocumentRenderer defRender = new DocumentRenderer(document, false);

It is very similar for ColumnDocumentRenderer.

Next, as you have switched off automatic flushing and change renderers by yourself, in the end of your code, just before document.close();, you would have to manually flush your renderers:

defRender.flush();
dictRender.flush();

Now the content appears, though still the result may look ugly because of content overlapping. This is up to the developer to resolve, because two renderers are independent instances and they maintain currentArea independent of each other. To handle this appropriately, you would have to update currentArea of the renderer you are going to switch to with the currentArea of the previous renderer you have just finished working with. You can do that by extending the standard provided renderers, or calling renderer.getCurrentArea() and modifying the bBox.