You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用iTextPdf7.2.6生成大PDF表格遇循环/空指针问题求助

iTextPdf 7.2.6 大表格生成问题解决

问题描述

使用iTextPdf 7.2.6生成跨页大表格时,遇到两个问题:

  • 复用渲染器处理溢出内容时触发NullPointerException
  • 每次重新创建渲染器会陷入无限循环

代码示例

public class App 
{
  public static void main(String[] args) {
    try {
        PdfWriter writer = new PdfWriter("test.pdf");
        PdfDocument pdf = new PdfDocument(writer);
        pdf.addNewPage();
        Document document = new Document(pdf);
        Table table = new Table(UnitValue.createPercentArray(new float[]{1, 1}));
        table.setWidth(UnitValue.createPercentValue(100));
        PdfFont font = PdfFontFactory.createFont(StandardFonts.HELVETICA, PdfEncodings.WINANSI, EmbeddingStrategy.FORCE_NOT_EMBEDDED);
        for(int i=0; i<10; i=i+2) {
          table.addCell(new Cell().add(new Paragraph("Lorem Ipsum").setFont(font)));
          table.addCell(new Cell().add(new Paragraph("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.").setFont(font)));
        }
        // 切换调用下面两个方法测试
        // integrateTableToPDF(table, new Rectangle(PageSize.A4), pdf);
        integrateTableToPDF2(table, new Rectangle(PageSize.A4), pdf);
        document.close();
        System.out.println("OK");
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
  
  private static void integrateTableToPDF(Table table, Rectangle pagesize, PdfDocument doc) throws IOException {
        float marginLeft = 60;
        float marginRight = 20;
        float marginBottom = 50;
        float usableWidth = pagesize.getWidth() - marginLeft - marginRight;
        float usableHeight = pagesize.getHeight() - marginBottom;
        Rectangle drawArea = new Rectangle(marginLeft, marginBottom, usableWidth, usableHeight);
        PdfPage page = doc.getLastPage();
        PdfCanvas pdfCanvas = new PdfCanvas(page);
        Canvas canvas = new Canvas(pdfCanvas, drawArea);
        TableRenderer renderer = (TableRenderer) table.createRendererSubTree();
        renderer.setParent(canvas.getRenderer());
        LayoutResult result = renderer.layout(new LayoutContext(new LayoutArea(doc.getNumberOfPages()-1, drawArea)));
        renderer.draw(new DrawContext(doc, pdfCanvas));
        while (result.getStatus() != LayoutResult.FULL) {
            TableRenderer overflowRenderer = (TableRenderer) result.getOverflowRenderer();
            PdfPage newPage = doc.addNewPage();
            PdfCanvas newCanvas = new PdfCanvas(newPage);
            Rectangle nextDrawArea = new Rectangle(marginLeft, marginBottom, usableWidth, pagesize.getHeight() - marginBottom);
            LayoutArea newArea = new LayoutArea(doc.getNumberOfPages()-1, nextDrawArea);
            Canvas nextCanvasModel = new Canvas(newCanvas, nextDrawArea);
            overflowRenderer.setParent(nextCanvasModel.getRenderer());
            result = overflowRenderer.layout(new LayoutContext(newArea));
            overflowRenderer.draw(new DrawContext(doc, newCanvas));
            nextCanvasModel.flush();
            nextCanvasModel.close();
        }
        canvas.flush();
        canvas.close();
    }
  
  private static void integrateTableToPDF2(Table table, Rectangle pagesize, PdfDocument doc) throws IOException {
        float marginLeft = 60;
        float marginRight = 20;
        float marginBottom = 50;
        float usableWidth = pagesize.getWidth() - marginLeft - marginRight;
        float usableHeight = pagesize.getHeight() - marginBottom;
        Rectangle drawArea = new Rectangle(marginLeft, marginBottom, usableWidth, usableHeight);
        PdfPage page = doc.getLastPage();
        PdfCanvas pdfCanvas = new PdfCanvas(page);
        Canvas canvas = new Canvas(pdfCanvas, drawArea);
        TableRenderer renderer = (TableRenderer) table.createRendererSubTree();
        renderer.setParent(canvas.getRenderer());
        LayoutResult result = renderer.layout(new LayoutContext(new LayoutArea(doc.getNumberOfPages()-1, drawArea)));
        renderer.draw(new DrawContext(doc, pdfCanvas));
        while (result.getStatus() != LayoutResult.FULL) {
            PdfPage newPage = doc.addNewPage();
            PdfCanvas newCanvas = new PdfCanvas(newPage);
            Rectangle nextDrawArea = new Rectangle(marginLeft, marginBottom, usableWidth, pagesize.getHeight() - marginBottom);
            LayoutArea newArea = new LayoutArea(doc.getNumberOfPages()-1, nextDrawArea);
            Canvas nextCanvasModel = new Canvas(newCanvas, nextDrawArea);
            renderer = (TableRenderer) table.createRendererSubTree();           
            renderer.setParent(nextCanvasModel.getRenderer());
            result = renderer.layout(new LayoutContext(newArea));
            renderer.draw(new DrawContext(doc, newCanvas));
            nextCanvasModel.flush();
            nextCanvasModel.close();
            if(doc.getNumberOfPages()==20)
              throw new IOException("Infinite loop !");
        }
        canvas.flush();
        canvas.close();
    }
}

测试说明

修改main方法中的调用,切换integrateTableToPDF与integrateTableToPDF2来测试两种方案。

报错信息

调用integrateTableToPDF时的报错

java.lang.NullPointerException
    at com.itextpdf.layout.renderer.AbstractRenderer.isFirstOnRootArea(AbstractRenderer.java:2380)
    at com.itextpdf.layout.renderer.TableRenderer.layout(TableRenderer.java:294)
    at com.itextpdftest.App.integrateTableToPDF(App.java:71)
    at com.itextpdftest.App.main(App.java:41)

调用integrateTableToPDF2时的报错

com.itextpdf.io.exceptions.IOException: Infinite loop !
    at com.itextpdftest.App.integrateTableToPDF2(App.java:107)
    at com.itextpdftest.App.main(App.java:41)

解决方案

最简方案:使用iText内置的自动跨页处理

iText本身已经支持表格自动跨页,不需要手动处理渲染器。直接通过Document.add()添加表格即可:

// 设置文档边距,对应你定义的margin值
document.setMargins(0, marginRight, marginBottom, marginLeft);
document.add(table);

自定义Canvas场景的修正方案

如果必须手动使用Canvas处理,正确的溢出处理逻辑如下:

private static void integrateTableToPDF(Table table, Rectangle pagesize, PdfDocument doc) throws IOException {
    float marginLeft = 60;
    float marginRight = 20;
    float marginBottom = 50;
    float usableWidth = pagesize.getWidth() - marginLeft - marginRight;
    float usableHeight = pagesize.getHeight() - marginBottom;
    Rectangle drawArea = new Rectangle(marginLeft, marginBottom, usableWidth, usableHeight);

    TableRenderer renderer = (TableRenderer) table.createRendererSubTree();
    LayoutResult result;
    int pageNumber = doc.getNumberOfPages();

    do {
        PdfPage page = doc.getPage(pageNumber);
        PdfCanvas pdfCanvas = new PdfCanvas(page);
        Canvas canvas = new Canvas(pdfCanvas, drawArea);
        renderer.setParent(canvas.getRenderer());
        
        LayoutContext context = new LayoutContext(new LayoutArea(pageNumber - 1, drawArea));
        result = renderer.layout(context);
        
        renderer.draw(new DrawContext(doc, pdfCanvas));
        canvas.flush();
        canvas.close();

        if (result.getStatus() != LayoutResult.FULL) {
            renderer = (TableRenderer) result.getOverflowRenderer();
            doc.addNewPage();
            pageNumber++;
        }
    } while (result.getStatus() != LayoutResult.FULL);
}

关键修正点

  • 复用LayoutResult返回的overflowRenderer,而非重新创建渲染器(避免无限循环)
  • 为每个新页面创建独立的Canvas和父渲染器,避免空指针
  • 正确管理页面编号,确保LayoutArea的页码参数准确

内容的提问来源于stack exchange,提问作者pacataque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 22:07:33