如何在同一页面切换不同的 iText 文档渲染器

How to switch different iText document renderers in the same page

案例是在Page开始的时候,Elements应该画成一列,之后,同一页的元素应该画成两列。

至此,根据iText示例“c02e10_jekyllhydev6”,我只能在页面之间切换不同的渲染器,即先应用DocumentRenderer,然后添加下一页的AreaBreak,并在新页面应用ColumnDocumentRenderer .

代码:

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();

如果文档中没有添加AreaBreak,不同渲染器的内容会重叠

根据 Alexey 在 中的评论,似乎可以在同一页面中切换不同的渲染器而不会出现内容重叠。

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.

但我不知道如何根据上面的指南实现它。

在您的情况下 ColumnDocumentRenderer 没有必要 - 该渲染器是为您想要在一页范围内的列中布置内容然后移动到下一页等情况而创建的。在您的情况下,您将内容布置在跨越许多页面的列中,这相当于只为 DocumentRenderer 设置适当的边距,而不是将列传递给 ColumnDocumentRenderer.

要临时切换渲染器,您确实需要调整它们的 currentArea 字段和 currentPageNumber。请注意,不能保证以下解决方案适用于所有复杂情况和所有 iText 版本。这只是关于如何实施您需要的指南,而不是适用于所有情况的完整解决方案。

我们需要的辅助渲染器 class 非常简单 - 它允许自定义当前区域:

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();
    }
}

现在我已经修改了您的代码以摆脱 ColumnDocumentRenderer 并改用普通的 DocumentRenderer。您只需要调整文档页边距并正确重新计算当前区域(留在页面上的 space):

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();

结果如下: