0

对于某些 PDF,我在刷新标签结构时看到了 NPE。iText 7.0.2-SNAPSHOT 会出现此问题。iText 5.5.10 可以很好地处理这些文件。NPE 被抛出,PdfDictionary.get(PdfName key, boolean asDirect)因为映射为空。该类中的地图唯一可以变为 null 的时间releaseContent()是被调用的时间。

由于releaseContent()' 的唯一目的是 - 据我所知 - 释放内存,因此我测试了将其更改为空方法时会发生什么。结果是文件似乎被正常处理。没有更多的例外。是一个示例文件。

释放后只有少数对象被访问。对于上面的示例文件,这也可以解决问题:

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
4

1 回答 1

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 6888 0 R,然后在找到第二个引用时失败。

我并不是很喜欢结构树,因此,我不确定在结构树中是否可以重复条目(毕竟它被称为,这可能表明给定节点应在其中引用为一个孩子只有一次)。但是可以通过将上述方法更改为

private void flushAllKids(IPdfStructElem elem) {
    for (IPdfStructElem kid : elem.getKids()) {
        if (kid instanceof PdfStructElem) {
            if (!((PdfStructElem) kid).isFlushed())
            {
                flushAllKids(kid);
                ((PdfStructElem) kid).flush();
            }
        }
    }
}

通过此更改,示例文档将毫无例外地被盖章。

于 2016-10-10T09:55:43.130 回答