The case is that at the beginning of Page, Elements should be drawn in one column, and after that, elements in the same page should be drawn in two columns.
So far, according to the iText example "c02e10_jekyllhydev6", I just can switch different renderers between pages, which means first applying DocumentRenderer, then add AreaBreak of Next Page, and applying ColumnDocumentRenderer in the new page.
The code:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc, PageSize.A4);
doc.SetMargins(36, 36, 36, 36);
Paragraph p = new Paragraph();
p.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p.Add(new Text(i + " "));
}
doc.Add(p);
**doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));**
PageSize ps = PageSize.A4;
var effectiveArea = doc.GetPageEffectiveArea(PageSize.A4);
float columnHeight = effectiveArea.GetHeight();
//Define column areas
Rectangle[] columns = new Rectangle[] {
new Rectangle(36, 36, 200, columnHeight),
new Rectangle(36 + 200 + 20, 36, effectiveArea.GetWidth()- 200 - 20, columnHeight)
};
ColumnDocumentRenderer renderer1 = new ColumnDocumentRenderer(doc, new Rectangle[] { columns[0] });
doc.SetRenderer(renderer1);
**doc.Add(new AreaBreak(AreaBreakType.LAST_PAGE));**
Paragraph p1 = new Paragraph();
p1.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p1.Add(new Text(i + " "));
}
doc.Add(p1);
ColumnDocumentRenderer renderer2 = new ColumnDocumentRenderer(doc, new Rectangle[] { columns[1] });
doc.SetRenderer(renderer2);
Paragraph p2 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p2.Add(new Text(i + " "));
}
doc.Add(p2);
doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
DocumentRenderer renderer3 = new DocumentRenderer(doc);
doc.SetRenderer(renderer3);
doc.Add(new AreaBreak(AreaBreakType.LAST_PAGE));
Paragraph p3 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p3.Add(new Text(i + " "));
}
doc.Add(p3);
doc.Close();
If no AreaBreak added to the document, the contents with different renderers will be overlapped.
From Alexey's comment in this case, It seems possible that switching different renderers in the same page without content overlapping.
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.
But I don't know how to achieve it according to above guides.
There is no need in ColumnDocumentRenderer
in your case - that renderer was created for cases when you want to lay out your content in columns in scope of one page and then move on to the next page etc. In your case you are laying out the content in columns that span many pages and that is equivalent to just setting proper margins to the DocumentRenderer
instead of passing the columns to ColumnDocumentRenderer
.
To switch the renderers ad-hoc you indeed need to tweak their currentArea
field and currentPageNumber
as well. Please note that the solution below is not guaranteed to work in all complex cases and in all iText versions. This is merely a guideline on how to implement what you need and not a complete solution to all cases.
The helper renderer class we need is very simple - it allows customizing current area:
private class ExtendedDocumentRenderer : DocumentRenderer {
public ExtendedDocumentRenderer(Document document, RootLayoutArea currentArea) : base(document) {
this.currentArea = new RootLayoutArea(currentArea.GetPageNumber(), currentArea.GetBBox().Clone());
this.currentPageNumber = this.currentArea.GetPageNumber();
}
}
Now I've adapted your code to get rid of the ColumnDocumentRenderer
and use plain DocumentRenderer
instead. You just need to tweak document margins and recalculate current area (the space that is left on the page) properly:
Document doc = new Document(pdfDocument);
Paragraph p = new Paragraph();
p.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p.Add(new Text(i + " "));
}
doc.Add(p);
RootLayoutArea endOfFullWidthContentArea = (RootLayoutArea) doc.GetRenderer().GetCurrentArea();
ExtendedDocumentRenderer renderer1 = new ExtendedDocumentRenderer(doc,
new RootLayoutArea(endOfFullWidthContentArea.GetPageNumber(), endOfFullWidthContentArea.GetBBox().Clone().SetWidth(200)));
doc.SetRightMargin(doc.GetRightMargin() + doc.GetPageEffectiveArea(PageSize.A4).GetWidth() - 200);
doc.SetRenderer(renderer1);
Paragraph p1 = new Paragraph();
p1.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 500; i++)
{
p1.Add(new Text(i + " "));
}
doc.Add(p1);
ExtendedDocumentRenderer renderer2 = new ExtendedDocumentRenderer(doc,
new RootLayoutArea(endOfFullWidthContentArea.GetPageNumber(),
endOfFullWidthContentArea.GetBBox().Clone().MoveRight(200)
.SetWidth(endOfFullWidthContentArea.GetBBox().GetWidth() - 200)));
doc.SetRightMargin(36);
doc.SetLeftMargin(200 + 36);
doc.SetRenderer(renderer2);
Paragraph p2 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p2.Add(new Text(i + " "));
}
doc.Add(p2);
// Compute which free area is lower in the document
RootLayoutArea areaColumn1 = (RootLayoutArea) renderer1.GetCurrentArea();
RootLayoutArea areaColumn2 = (RootLayoutArea) renderer2.GetCurrentArea();
RootLayoutArea downArea = areaColumn1.GetPageNumber() > areaColumn2.GetPageNumber() ? areaColumn1 :
(areaColumn1.GetPageNumber() < areaColumn2.GetPageNumber() ? areaColumn2 :
(areaColumn1.GetBBox().GetTop() < areaColumn2.GetBBox().GetTop() ? areaColumn1 : areaColumn2));
doc.SetMargins(36, 36, 36, 36);
DocumentRenderer renderer3 = new ExtendedDocumentRenderer(doc,
new RootLayoutArea(downArea.GetPageNumber(), downArea.GetBBox().Clone().SetX(36).SetWidth(doc.GetPageEffectiveArea(PageSize.A4).GetWidth())));
doc.SetRenderer(renderer3);
Paragraph p3 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
p3.Add(new Text(i + " "));
}
doc.Add(p3);
doc.Close();
Result looks as follows: