对于某些PDF,我会在刷新标签结构时看到NPE。 iText 7.0.2-SNAPSHOT会出现此问题。 iText 5.5.10处理这些文件就好了。 NPE在PdfDictionary.get(PdfName key, boolean asDirect)
中抛出,因为映射为空。
该类中的映射唯一可以变为null的时间是调用releaseContent()
时。
由于releaseContent()
的唯一目的是 - 据我所知 - 释放内存,我已经测试了当它变为空方法时会发生什么。
结果是文件似乎正常处理。没有例外。 Here是一个示例文件。
释放后只能访问少量对象。对于上面的示例文件,这也可以解决问题:
protected void releaseContent() {
List<Integer> objs = Arrays.asList(6888, 6856, 6824, 844, 836);
if (objs.contains(indirectReference.objNr)) {
return;
}
map = null;
}
我会将为什么的分析留给那些比我更了解的人。无论这是PDF格式错误还是iText7中的错误,我都不知道。
我在用iText 7.0.2-SNAPSHOT做什么:
PdfReader reader = new PdfReader(src);
PdfWriter writer = new PdfWriter(dest);
PdfDocument doc = new PdfDocument(reader, writer);
doc.close();`
输出结果是什么:
Exception in thread "main" com.itextpdf.kernel.PdfException: Tag structure flushing failed: it might be corrupted.
at com.itextpdf.kernel.pdf.PdfDocument.tryFlushTagStructure(PdfDocument.java:1746)
at com.itextpdf.kernel.pdf.PdfDocument.close(PdfDocument.java:727)
at perinorm.cleanPdf.MainCleanPDF.run(MainCleanPDF.java:139)
at perinorm.cleanPdf.MainCleanPDF.lambda$2(MainCleanPDF.java:58)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
at java.util.Iterator.forEachRemaining(Unknown Source)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Unknown Source)
at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
at java.util.stream.ReferencePipeline.forEach(Unknown Source)
at perinorm.cleanPdf.Main.main(Main.java:56)
Caused by: java.lang.NullPointerException
at com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:555)
at com.itextpdf.kernel.pdf.PdfDictionary.get(PdfDictionary.java:146)
at com.itextpdf.kernel.pdf.tagging.PdfStructElem.getK(PdfStructElem.java:338)
at com.itextpdf.kernel.pdf.tagging.PdfStructElem.getKids(PdfStructElem.java:322)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flushAllKids(PdfStructTreeRoot.java:247)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flushAllKids(PdfStructTreeRoot.java:249)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flushAllKids(PdfStructTreeRoot.java:249)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flushAllKids(PdfStructTreeRoot.java:249)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flushAllKids(PdfStructTreeRoot.java:249)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flushAllKids(PdfStructTreeRoot.java:249)
at com.itextpdf.kernel.pdf.tagging.PdfStructTreeRoot.flush(PdfStructTreeRoot.java:184)
at com.itextpdf.kernel.pdf.PdfDocument.tryFlushTagStructure(PdfDocument.java:1744)
... 16 more
答案 0 :(得分:0)
此问题的直接原因是在示例文档的结构树中,一些节点被多次使用,例如。
6770 0 obj
<<
/K [ 6873 0 R 6874 0 R 6875 0 R 6876 0 R 6877 0 R 6878 0 R 6879 0 R
6880 0 R 6881 0 R 6882 0 R 6883 0 R 6884 0 R 6885 0 R 6886 0 R
6887 0 R 6888 0 R 6888 0 R ]
/P 5874 0 R
/S /TR
>>
如您所见,6888 0 R
在此结构树节点的子阵列中出现两次。
当iText 7关闭PdfDocument
时,它会遍历结构树并将找到的每个元素刷新到目标文档:
private void flushAllKids(IPdfStructElem elem) {
for (IPdfStructElem kid : elem.getKids()) {
if (kid instanceof PdfStructElem) {
flushAllKids(kid);
((PdfStructElem) kid).flush();
}
}
}
(来自PdfStructTreeRoot
)
因此,在手头的文档中,它会在找到第一个6888 0 R
引用时刷新子字典对象6888 0,然后在找到第二个引用时失败。
我并没有真正进入结构树,因此,我不确定在结构树中是否可以重复输入(毕竟它被称为 tree 可以指示给定节点在其中仅作为子项引用一次)。但是可以通过将上面的方法更改为
来强化iText来对抗这些重复使用的节点private void flushAllKids(IPdfStructElem elem) {
for (IPdfStructElem kid : elem.getKids()) {
if (kid instanceof PdfStructElem) {
if (!((PdfStructElem) kid).isFlushed())
{
flushAllKids(kid);
((PdfStructElem) kid).flush();
}
}
}
}
通过此更改,样本文档会被标记,没有例外。