切换文档渲染器 - 无法在已刷新的页面上绘制元素

Switch document renderer - Cannot draw elements on already flushed pages

根据 Alexey 的回答,我们可以利用不同的渲染器和自定义的 RootLayoutArea 来实现如下布局行为。

代码如下:

public void ManipulatePdf(String dest)
{
    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);

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

    //The paragraph drawn in the left
    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 + 20)
                .SetWidth(endOfFullWidthContentArea.GetBBox().GetWidth() - 200 - 20)));
    doc.SetRightMargin(36);
    doc.SetLeftMargin(200 + 36 + 20);
    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();
}

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

我们可以看到左栏的Paragraph 1跨了两页,Paragraph 2跨了三页,在这种情况下代码运行良好。

但是如果我们在第 1 段中添加更多内容并使其跨越 3 页如下:

//The paragraph drawn in the left has more contents
Paragraph p1 = new Paragraph();
p1.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 800; i++)
{
    p1.Add(new Text(i + " "));
}
doc.Add(p1);

Paragraph 2渲染时抛出“无法在已经刷新的页面上绘制元素”的异常。如何解决这个问题呢?谢谢!

如果您的元素可以跨越多个页面,那么您需要应用 DocumentRendererimmediateFlush=false 设置,该设置会转到其构造函数。

您需要按以下方式修改您的 ExtendedDocumentRenderer 自定义实现:

private class ExtendedDocumentRenderer : DocumentRenderer
{
    public ExtendedDocumentRenderer(Document document, RootLayoutArea currentArea) : base(document, false)
    {
        this.currentArea = new RootLayoutArea(currentArea.GetPageNumber(), currentArea.GetBBox().Clone());
        this.currentPageNumber = this.currentArea.GetPageNumber();
    }
}

然后,每当您完成另一个文档呈现器时,您都需要调用 Document#Flush。本质上,在您的代码示例中,需要在添加每个新元素后进行调用。这是完整的代码:

Document doc = new Document(pdfDocument, 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);

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

//The paragraph drawn in the left
Paragraph p1 = new Paragraph();
p1.SetBorder(new SolidBorder(0.5f));
for (int i = 1; i <= 800; i++)
{
    p1.Add(new Text(i + " "));
}
doc.Add(p1);
doc.Flush();

ExtendedDocumentRenderer renderer2 = new ExtendedDocumentRenderer(doc,
    new RootLayoutArea(endOfFullWidthContentArea.GetPageNumber(),
        endOfFullWidthContentArea.GetBBox().Clone().MoveRight(200 + 20)
            .SetWidth(endOfFullWidthContentArea.GetBBox().GetWidth() - 200 - 20)));
doc.SetRightMargin(36);
doc.SetLeftMargin(200 + 36 + 20);
doc.SetRenderer(renderer2);
Paragraph p2 = new Paragraph();
for (int i = 1; i <= 1000; i++)
{
    p2.Add(new Text(i + " "));
}
doc.Add(p2);
doc.Flush();

//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.Flush();

doc.Close(); 

经过之前踩过的坑,可以尝试调用三参构造函数,并设置immediateflush为false

 PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
 // setting immediateflush to false 
 Document doc = new Document(pdfDoc, PageSize.A4, false);