【问题标题】:How to switch different iText document renderers in the same page如何在同一页面中切换不同的 iText 文档渲染器
【发布时间】:2020-06-17 17:10:53
【问题描述】:

情况是,在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 在this case 的评论来看,似乎可以在同一页面中切换不同的渲染器而不会发生内容重叠。

要妥善处理此问题,您必须将要切换到的渲染器的 currentArea 更新为您刚刚完成的前一个渲染器的 currentArea。您可以通过扩展标准提供的渲染器,或调用 renderer.getCurrentArea() 并修改 bBox 来做到这一点。

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

【问题讨论】:

    标签: c# itext itext7


    【解决方案1】:

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

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

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

    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。您只需要调整文档边距并正确重新计算当前区域(页面上剩余的空间):

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

    结果如下:

    【讨论】:

    • 这真是一个了不起的实现,利用RootLayoutArea来灵活地控制布局。真诚的感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-26
    • 2023-03-17
    • 2015-01-07
    相关资源
    最近更新 更多