我使用Bruno的示例(https://developers.itextpdf.com/examples/form-examples/clone-create-fields-table)将表单字段添加到新创建的文档中的表中。示例本身没有任何问题,但如果我想创建50个表,我会得到以下异常:
Exception in thread "main" java.lang.NullPointerException
at com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:552)
at com.itextpdf.kernel.pdf.PdfDictionary.getAsArray(PdfDictionary.java:156)
at com.itextpdf.kernel.pdf.PdfPage.getAnnotations(PdfPage.java:743)
at com.itextpdf.kernel.pdf.annot.PdfAnnotation.getPage(PdfAnnotation.java:427)
at com.itextpdf.forms.fields.PdfFormField.regenerateField(PdfFormField.java:1781)
at com.itextpdf.forms.fields.PdfFormField.setValue(PdfFormField.java:1043)
at com.itextpdf.forms.fields.PdfFormField.setValue(PdfFormField.java:1004)
at com.itextpdf.forms.fields.PdfFormField.setValue(PdfFormField.java:999)
at com.itextpdf.forms.fields.PdfFormField.createText(PdfFormField.java:469)
at com.itextpdf.forms.fields.PdfFormField.createText(PdfFormField.java:410)
at com.itextpdf.forms.fields.PdfFormField.createText(PdfFormField.java:391)
at CreateFormInTable$MyCellRenderer.draw(CreateFormInTable.java:62)
at com.itextpdf.layout.renderer.TableRenderer.drawChildren(TableRenderer.java:1023)
at com.itextpdf.layout.renderer.AbstractRenderer.draw(AbstractRenderer.java:458)
at com.itextpdf.layout.renderer.TableRenderer.draw(TableRenderer.java:948)
at com.itextpdf.layout.renderer.DocumentRenderer.flushSingleRenderer(DocumentRenderer.java:138)
at com.itextpdf.layout.renderer.RootRenderer.processRenderer(RootRenderer.java:349)
at com.itextpdf.layout.renderer.RootRenderer.shrinkCurrentAreaAndProcessRenderer(RootRenderer.java:338)
at com.itextpdf.layout.renderer.RootRenderer.addChild(RootRenderer.java:236)
at com.itextpdf.layout.RootElement.add(RootElement.java:109)
at com.itextpdf.layout.Document.add(Document.java:143)
at CreateFormInTable.manipulatePdf(CreateFormInTable.java:45)
at CreateFormInTable.main(CreateFormInTable.java:25)
我在Windows 8 Enterprise上使用Java 1.8.0_121。
修改后的manipulatePdf
方法如下所示:
protected void manipulatePdf(String dest) throws Exception {
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
for (int i = 0; i < 50; i++) {
System.out.println(i);
Table table = new Table(2);
Cell cell;
cell = new Cell().add("Name:");
table.addCell(cell);
cell = new Cell();
cell.setNextRenderer(new MyCellRenderer(cell, "name" + i));
table.addCell(cell);
cell = new Cell().add("Address");
table.addCell(cell);
cell = new Cell();
cell.setNextRenderer(new MyCellRenderer(cell, "address" + i));
table.addCell(cell);
doc.add(table);
}
doc.close();
}
可以在此处找到生成NPE的完整示例:https://github.com/trettstadtnlb/itext-table-form-fields
有没有人遇到过这种行为或者可以确认这是一个错误?
答案 0 :(得分:3)
使用更少的内容很容易在iText 7.0.x中重现此行为:
PdfDocument pdfDoc = new PdfDocument(new PdfWriter(dest));
Document doc = new Document(pdfDoc);
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
doc.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
PdfTextFormField field = PdfFormField.createText(pdfDoc, new Rectangle(100, 100, 300, 20), "name", "");
PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
form.addField(field);
doc.close();
这背后的问题是,对于没有关联页面的表单字段(窗口小部件),iText尝试通过迭代所有文档页面并查找其各自的注释数组来确定窗口小部件所在的页面。它在createText
期间已经这样做了,同时设置了字段值(并更新了小部件外观)...
但是,在上面的代码中,先前新创建的页面(至少是第一个页面)已经刷新到写入程序并在内存中取消初始化。因此,页面上的循环尝试从去初始化的页面字典中检索值,即从null Map
中检索,因此NullPointerException
。
这是iText中的错误还是反模式,我不知道。
受@ SamuelHuylebroeck评论的启发,我尝试了OP的原始代码和上面的简化代码,immediateFlush
设置为false
。实际上,这可以防止异常,简化代码的结果看起来不错。然而,OP原始代码的结果并不像他预期的那样,表格是在四个页面上构建的,但所有字段都出现在最后一页上!
因此,如果直接创建表单元素(即不通过事件),将immediateFlush
设置为false
可能会被视为解决方法(但 not修复因为早期刷新是iText的一个重要特征)。然而,表单创建的一般情况被打破了,除非有人能指出这里的代码是iText 7反模式并且可以显示它应该如何完成。