【问题标题】:Switch document renderer - Cannot draw elements on already flushed pages切换文档渲染器 - 无法在已刷新的页面上绘制元素
【发布时间】:2020-06-20 07:08:52
【问题描述】:

根据this caseAlexey 的回答,我们可以利用不同的渲染器和自定义的 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();
    }
}

我们可以看到左栏的第1段跨越两页,第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);

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

【问题讨论】:

    标签: c# itext itext7


    【解决方案1】:

    如果您的元素可以跨越多个页面,那么您需要将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(); 
    

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 2020-01-17
        • 1970-01-01
        • 2020-01-22
        • 1970-01-01
        • 2020-08-12
        • 1970-01-01
        • 1970-01-01
        • 2020-01-23
        • 1970-01-01
        相关资源
        最近更新 更多